package com.ratta.supernote.plugin.sticker.lib.view.editText.editor;

import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.text.Editable;
import android.text.InputType;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

import androidx.annotation.NonNull;


import com.ratta.supernote.plugin.sticker.lib.view.dashView.DashVerticalView;
import com.ratta.supernote.plugin.sticker.lib.view.editText.MenuItemView;
import com.ratta.supernote.plugin.sticker.lib.view.editText.SelectionView;

import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;

@SuppressLint("NewApi")
public class RattaEditor {
    private final String TAG = "RattaEditor";

    private final TextView mTextView;

    private GestureDetector mGestureDetector;

    private int mSelStart = 0;
    private int mSelEnd = 0;
    // 光标的位置
    private int mCursorIndex;
    private Paint mHighlightPaint;

    SelHandleView mStartHandleView;
    SelHandleView mEndHandleView;
    // 单独的光标拖柄
    SelHandleView mCursorHandleView;
    private boolean isDownStartHandle;
    private boolean isDownEndHandle;
    private boolean isDownCursorHandle;
    private boolean isDownSelectionMenu;

    // 是否拖动开始的
    public boolean isDraggingStart = false;

    private LinearLayout mSelectionView;
    private List<View> mBaseSelectMenuList = new ArrayList<>();

    private View selectAllView;
    private View pasteView;

    // 选中菜单
    private PopupWindow mSelectionMenu;


    ClipboardManager mClipboard;
    final int[] mTempCoords = new int[2];
    private int mPositionX, mPositionY;
    private int mPositionXOnScreen, mPositionYOnScreen;

    private final int[] mTextViewLocationOffset = new int[2];

    RattaEditorListener mRattaEditorListener;

    RattaEditorActionListener mRattaEditorActionListener;

    //上一次滑动的距离
    private int lastScrollY = 0;
    private int lastScrollX = 0;

    // 光标的x轴坐标
    private CursorRect mCursorRect;

    // 光标手柄消失的disposable
    Disposable cursorHandleDisposable = null;

    // 上一次的对齐方式
    private int lastGravity;

    private String resDir;

    private Bitmap selStartBitmap;
    private Bitmap selEndBitmap;
    private Bitmap cursorBitmap;

	// 上一次点击的位置
    private PointF lastDownPoint;
    boolean isCursorTail = false;

    private Paint mPaint;

    private int action = MotionEvent.ACTION_UP;

    private String beforeTextStr = null;
    private boolean isTextSelected = false;

    boolean selectionMenuEnabled = true;
    boolean cursorMenuEnabled = true;
    boolean cursorHandleEnabled = true;

    // 所有行高亮区域的路径列表，下表是行号
    private ConcurrentHashMap<Integer, HighLightLineInfo> mHighLightMap= new ConcurrentHashMap<Integer, HighLightLineInfo>() {};


    private final PublishSubject<CharSequence> textChangedSubject = PublishSubject.create();
    private final CompositeDisposable disposables = new CompositeDisposable();


    private interface SelHandleListener {
        boolean onSelOffsetChange(int selOffset, boolean isBorder);

        void onDownHandle(boolean isDown);

        // 点击手柄
        void onClickHandle();

        // 触摸弹起
        void onTouchUp();

        // 更新手柄的菜单位置
        void onUpdateHandleMenuPosition(boolean isBorder);

    }

    public interface RattaEditorListener {

        /**
         * 是否显示选中菜单
         * @param isShow
         * @param menuLocation 选中菜单相对屏幕的位置
         * @param width 菜单宽度
         * @param height 菜单高度
         *
         */
        void onShowSelection(boolean isShow, int[] menuLocation, int width, int height);
    }

    // 监听文本框的的编辑操作
    public interface RattaEditorActionListener {
        void onCopy(String value);

        void onCut(String value);

        void onPaste(CharSequence value);
    }

    public RattaEditor(TextView textView) {
        this.mTextView = textView;
        init();
    }


    private void init() {
        resDir = mTextView.getContext().getApplicationContext().getFilesDir().getAbsolutePath()
                +"/plugins/a5db6166e8fe8e17/res/drawable/";
        initBitmap();

        // 绘制光标用的Paint
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);

//        mTextView.getLocationInWindow(mTextViewLocation);
        lastGravity = mTextView.getGravity();

        mClipboard = (ClipboardManager) mTextView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
        initSelectionMenu();
        initHandleView();
        mTextView.setHighlightColor(Color.TRANSPARENT);
        mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        // 设置TextView不可选中，因为要自定义绘制
        mTextView.setTextIsSelectable(true);
        // 禁用长按触发文本选择
        mTextView.setLongClickable(false);

        initTextViewListener();
        Disposable disposable = textChangedSubject.throttleLast(500, TimeUnit.MILLISECONDS)
                .observeOn(Schedulers.io())
                .subscribe(s->{
                    textChanged(s);
                });
        disposables.add(disposable);

    }



    // 初始化TextView的监听
    private void initTextViewListener(){
        // 消除TextView的菜单监听
        mTextView.setCustomInsertionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {

            }
        });

        // 消除TextView的选中菜单监听
        mTextView.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {

            }
        });

        // 监听滑动的距离
        mTextView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                int scrollY = mTextView.getScrollY();
                Log.i(TAG, "TextView addOnScrollChangedListener onScrollChanged scrollX:" + mTextView.getScrollX() + ", scrollY:" + scrollY);
                // 位置可能不对，需要更新
                if ((lastScrollY != scrollY || lastScrollX != mTextView.getScrollX())
                        && mSelectionMenu != null && mSelectionMenu.isShowing()) {
                    updateSelectionMenuPosition();

                }
            }
        });
        // 设置手势监听，主要是为了监听长按选中文字
        mGestureDetector = new GestureDetector(mTextView.getContext(), new GestureDetector.SimpleOnGestureListener() {
//            @Override
//            public void onLongPress(@NonNull MotionEvent e) {
//                handleLongPress(e.getX(), e.getY());
//            }


        });
        listenTextViewLayoutChange();

        // 监听字符串变化
        mTextView.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // 文字改变，需要清空lastDownPoint，防止光标变化
                String str = s.toString();
                if(!TextUtils.equals(beforeTextStr, str)) {
                    lastDownPoint = null;
                }
                beforeTextStr = str;

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                textChangedSubject.onNext(s);

            }

            @Override
            public void afterTextChanged(Editable s) {
                hideCursorHandle();
                dismissPopupView();

            }
        });
    }

    private void initBitmap() {
        selStartBitmap = EditorUtils.getSelStartBitmap(mTextView.getContext());
        selEndBitmap = EditorUtils.getSelEndBitmap(mTextView.getContext());
        cursorBitmap = EditorUtils.getCursorBitmap(mTextView.getContext());

    }

    private void listenTextViewLayoutChange() {
        mTextView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

                /*int[] tempLocation = new int[2];
                mTextView.getLocationInWindow(tempLocation);
                Log.i(TAG, "TextView onLayoutChange tempLocation:" + tempLocation[0] + "==" + tempLocation[1] + ", TextViewLocation："
                        + mTextViewLocation[0] + "==" + mTextViewLocation[1]);
                if (tempLocation[0] != mTextViewLocation[0] || tempLocation[1] != mTextViewLocation[1]) {
                    mTextViewLocation = tempLocation;
                    refreshSelectionView();
                }*/
                textChangedSubject.onNext(mTextView.getText());
            }
        });


        mTextView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mTextView.post(() -> {
                    int[] tempLocationScreen = new int[2];

                    mTextView.getLocationInWindow(tempLocationScreen);
                    Rect visibleRect = new Rect();
                    mTextView.getGlobalVisibleRect(visibleRect);
                    if(visibleRect.width() <=0 || visibleRect.height() <=0) {
                        return ;
                    }
                    mTextView.getLocationOnScreen(mTempCoords);
                    mPositionXOnScreen = mTempCoords[0];
                    mPositionYOnScreen = mTempCoords[1];
                    mTextViewLocationOffset[0] = mPositionXOnScreen - mPositionX;
                    mTextViewLocationOffset[1] = mPositionYOnScreen - mPositionY;

                    if (tempLocationScreen[0] != mPositionX || tempLocationScreen[1] != mPositionY) {


                        Log.i(TAG, "TextView onPreDraw mTextViewLocationScreen:" + Arrays.toString(mTempCoords));
                        mPositionX = tempLocationScreen[0];
                        mPositionY = tempLocationScreen[1];
                        Log.i(TAG, "TextView onPreDraw mPositionX:" + mPositionX +"=="+mPositionY);
                        refreshSelectionView();

                    }
                });

                return true;
            }
        });
    }

    private MenuItemView getMenuItem(String text) {
        float fountSize = mTextView.getContext().getResources().getDimension(com.ratta.supernote.note.plugincore.R.dimen.dp_px_34)
                / mTextView.getResources().getDisplayMetrics().scaledDensity;
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
//        layoutParams.leftMargin = mTextView.getContext().getResources().getDimensionPixelSize(R.dimen.dp_px_24);
//        layoutParams.rightMargin = mTextView.getContext().getResources().getDimensionPixelSize(R.dimen.dp_px_24);


        MenuItemView textView = new MenuItemView(mTextView.getContext());

        textView.setLayoutParams(layoutParams);
        textView.setText(text);
        textView.setGravity(Gravity.CENTER);
        textView.setEnabled(true);
        textView.setClickable(true);
        textView.setTextColor(Color.BLACK);
        textView.setPadding(mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_24),
                0,
                mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_24),
                0);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fountSize);
        return textView;
    }

    // 初始化分割线
    private View getMenuDivideView() {
        DashVerticalView dashView = new DashVerticalView(mTextView.getContext());
        dashView.setStrokeWidth(mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_1));
        dashView.setDashWidth(mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_2));
        dashView.setGapWidth(mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_2));
//        dashView.setVertical(true);
        dashView.setLayoutParams(new LinearLayout.LayoutParams(
                mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_1),
                mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_26)
        ));
//        dashView.setBackgroundColor(Color.BLACK);
        return dashView;
    }

    private void initSelectionMenu() {
        View menuDivideView = getMenuDivideView();
        initSelectMenuList();
        // 初始化PopupWindow
        mSelectionView = new SelectionView(mTextView.getContext());
        for (int i = 0; i < mBaseSelectMenuList.size(); i++) {
            mSelectionView.addView(mBaseSelectMenuList.get(i));
            if (i < mBaseSelectMenuList.size() - 1) {
                mSelectionView.addView(menuDivideView);
            }
        }
        mSelectionView.setGravity(Gravity.CENTER);
        mSelectionView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_64)));
        mSelectionView.setOrientation(LinearLayout.HORIZONTAL);
        mSelectionView.setBackgroundColor(Color.WHITE);
        mSelectionMenu = new PopupWindow(mSelectionView,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_64));
        mSelectionMenu.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
        mSelectionMenu.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        mSelectionMenu.setOutsideTouchable(false);
        mSelectionMenu.setFocusable(false); // 禁止获取焦点
        mSelectionMenu.setTouchInterceptor(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "mSelectionMenu onInterceptTouchEvent touch:" + event.getAction());
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                    case MotionEvent.ACTION_OUTSIDE:
                        isDownSelectionMenu = isDownSelectionMenu(event);
//                        if (!isDownStartHandle && !isDownEndHandle && !isDownSelectionMenu) {
////                            Selection.setSelection((Spannable) mTextView.getText(), mSelStart);
//                            dismissPopupView();
//                        }
                        break;

                }
                return false;
            }
        });

        // 修改PopupWindow配置
        mSelectionMenu.setAnimationStyle(0); // 禁用动画
        mSelectionMenu.setClippingEnabled(false); // 禁用自动裁剪
    }

    private void initSelectMenuList() {
        String lang = mTextView.getResources().getConfiguration().getLocales().get(0).toString();
        View copyView = getMenuItem(LangUtils.getString(LangUtils.text_copy, lang));
        copyView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int selStart = Math.min(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
                int selEnd = Math.max(mTextView.getSelectionStart(), mTextView.getSelectionEnd());

                String textContent = mTextView.getText().toString();

                String subStr = textContent.substring(selStart, selEnd);

                // 可能有问题，需要检验
                ClipData clip = ClipData.newPlainText("text", subStr);
                mClipboard.setPrimaryClip(clip);
                Selection.setSelection((Spannable) mTextView.getText(), selEnd);
                dismissPopupView();
                if(mRattaEditorActionListener != null) {
                    mRattaEditorActionListener.onCopy(subStr);
                }
            }
        });
        mBaseSelectMenuList.add(copyView);

        if (mTextView instanceof EditText) {

            View cutView = getMenuItem(LangUtils.getString(LangUtils.text_cut, lang));
            cutView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int selStart = Math.min(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
                    int selEnd = Math.max(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
                    String subStr = "";
                    if (mTextView.getText() instanceof Editable) {
                        Editable editable = (Editable) mTextView.getText();
                        subStr = editable.toString().substring(mSelStart, mSelEnd);
                        editable.delete(selStart, selEnd);
                    } else {
                        String textContent = mTextView.getText().toString();
                        subStr = textContent.substring(mSelStart, mSelEnd);
                        StringBuilder newContent = new StringBuilder(textContent);
                        newContent.delete(mSelStart, mSelEnd);
                        mTextView.setText(newContent.toString());
                        if (mTextView instanceof EditText) {
                            ((EditText) mTextView).setSelection(selStart);
                        } else if (mTextView.getText() instanceof Spannable) {
                            Selection.setSelection((Spannable) mTextView.getText(),
                                    selStart);
                        }
                    }
                    // 可能有问题，需要检验
                    ClipData clip = ClipData.newPlainText("text", subStr);
                    mClipboard.setPrimaryClip(clip);
                    dismissPopupView();
                    if (mRattaEditorActionListener != null) {
                        mRattaEditorActionListener.onCut(subStr);
                    }
                }
            });
            mBaseSelectMenuList.add(cutView);

            // 需要判定剪切板是否有内容
            pasteView = getMenuItem(LangUtils.getString(LangUtils.text_paste, lang));
            pasteView.setId(View.generateViewId());
            pasteView.setOnClickListener(new View.OnClickListener() {
                @SuppressLint("NewApi")
                @Override
                public void onClick(View v) {

                    String clipText = getClipboardText().toString();
                    if (TextUtils.isEmpty(clipText)) {
                        return;
                    }
                    boolean isMultiLine = (mTextView.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
                    if (mTextView.getMaxLines() <= 1 || !isMultiLine) {
                        clipText = clipText.replace("\n", " ");
                        clipText = clipText.replace("\r", " ");

                    }
                    int selStart = Math.min(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
                    int selEnd = Math.max(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
                    // 检查是否是可编辑的对象
                    if (mTextView.getText() instanceof Editable) {
                        Editable editable = (Editable) mTextView.getText();
                        editable.replace(selStart, selEnd, clipText);
                        textChangedSubject.onNext(editable);
                        int cursorOffset = selStart + clipText.length();
                        if(cursorOffset > editable.length()) {
                            cursorOffset = editable.length();
                        }
                        ((EditText) mTextView).setSelection(cursorOffset);


                    } else {
                        StringBuilder textContent = new StringBuilder(mTextView.getText());
                        textContent.delete(selStart, selEnd);
                        textContent.insert(selStart, clipText);
                        mTextView.setText(textContent.toString());
                        int cursorOffset = selStart + clipText.length();
                        if(cursorOffset > mTextView.getText().length()) {
                            cursorOffset = mTextView.getText().length();
                        }
                        // 设置光标位置到粘贴文本的首位，末尾会有崩溃bug，因为文字溢出了
                        if (mTextView instanceof EditText) {
                            ((EditText) mTextView).setSelection(cursorOffset);
                        } else if (mTextView.getText() instanceof Spannable) {
                            Selection.setSelection((Spannable) mTextView.getText(),
                                    cursorOffset);
                        }
                    }
                    hideCursorHandle();
                    dismissPopupView();
                    if (mRattaEditorActionListener != null) {
                        mRattaEditorActionListener.onPaste(clipText);
                    }

                }
            });


        }
        selectAllView = getMenuItem(LangUtils.getString(LangUtils.text_select_all, lang));
        selectAllView.setId(View.generateViewId());
        selectAllView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSelStart = 0;
                mSelEnd = mTextView.getText().length();

                hideCursorHandle();
                showSelHandleView();
                showSelectionMenu(false);
                mTextView.postInvalidate();

            }
        });
    }

    // 检查是否有剪切板内容
    private boolean checkClipboard() {
        if (mClipboard.hasPrimaryClip()
                && mClipboard.getPrimaryClip() != null
                && mClipboard.getPrimaryClip().getItemCount() > 0) {
            ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0);
            CharSequence text = item.getText();
            if (!TextUtils.isEmpty(text)) {
                return true;
            }

        }

        return false;
    }

    // 获取剪切板的内容
    private CharSequence getClipboardText() {
        if (mClipboard.hasPrimaryClip()
                && mClipboard.getPrimaryClip() != null
                && mClipboard.getPrimaryClip().getItemCount() > 0) {
            ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0);
            CharSequence text = item.getText();
            if (!TextUtils.isEmpty(text)) {
                return text;
            }

        }
        return null;
    }

    private void initHandleView() {
        mCursorHandleView = new SelHandleView(mTextView.getContext(), cursorBitmap);
        mCursorHandleView.setHandleWidth(mTextView.getContext().getResources().getDimensionPixelSize(
                com.ratta.supernote.note.plugincore.R.dimen.dp_px_32
        ));
        mCursorHandleView.setHandleHeight(mTextView.getContext().getResources().getDimensionPixelSize(
                com.ratta.supernote.note.plugincore.R.dimen.dp_px_56
        ));
        mCursorHandleView.setCursorHandle(true);
        mCursorHandleView.setSelListener(new SelHandleListener() {
            @Override
            public boolean onSelOffsetChange(int selOffset, boolean isBorder) {
                if (selOffset == mCursorIndex) {
                    return false;
                }
                mCursorIndex = mSelStart = mSelEnd = selOffset;
                Selection.setSelection((Spannable) mTextView.getText(), mCursorIndex);
                mTextView.postInvalidate();
                return true;
            }

            @Override
            public void onDownHandle(boolean isDown) {
                isDownCursorHandle = true;
                disposeCursorHandle();
            }

            @Override
            public void onClickHandle() {
                if(cursorMenuEnabled) {
                    showSelectionMenu(true);
                    disposeCursorHandle();
                }

            }

            @Override
            public void onTouchUp() {
                isDownCursorHandle = false;
                if (!mSelectionMenu.isShowing()) {
                    cursorHandleTimeout();
                }
            }

            @Override
            public void onUpdateHandleMenuPosition(boolean isBorder) {
                if(mSelectionMenu.isShowing()) {
                    updateSelectionMenuPosition(isBorder);
                }


            }

        });

        mStartHandleView = new SelHandleView(mTextView.getContext(),
                selStartBitmap);
        mStartHandleView.setHandleWidth(mTextView.getContext().getResources().getDimensionPixelSize(
                com.ratta.supernote.note.plugincore.R.dimen.dp_px_32
        ));
        mStartHandleView.setHandleHeight(mTextView.getContext().getResources().getDimensionPixelSize(
                com.ratta.supernote.note.plugincore.R.dimen.dp_px_56
        ));
        mStartHandleView.setStart(true);
        mStartHandleView.setSelListener(new SelHandleListener() {
            @Override
            public boolean onSelOffsetChange(int selOffset, boolean isBorder) {
                // 开始位置和结束位置不能相同，相同就不更新手柄
                if (selOffset == mSelEnd) {
                    return false;
                }
                Log.i(TAG, "StartHandleView onSelOffsetChange selOffset:" + selOffset);

                mSelStart = selOffset;
                isDraggingStart = mSelStart < mSelEnd;
                updateSystemSelOffset();
                updateSelHandleDrawable();
                mTextView.postInvalidate();

                return true;
            }

            @Override
            public void onDownHandle(boolean isDown) {
                isDownStartHandle = isDown;

            }

            @Override
            public void onClickHandle() {

            }

            @Override
            public void onTouchUp() {
                isDownStartHandle = false;
                isDraggingStart = false;

            }

            @Override
            public void onUpdateHandleMenuPosition(boolean isBorder) {
                updateSelectionMenuPosition(isBorder);

            }
        });

        mEndHandleView = new SelHandleView(mTextView.getContext(),
                selEndBitmap);
        mEndHandleView.setHandleWidth(mTextView.getContext().getResources().getDimensionPixelSize(
                com.ratta.supernote.note.plugincore.R.dimen.dp_px_32
        ));
        mEndHandleView.setHandleHeight(mTextView.getContext().getResources().getDimensionPixelSize(
                com.ratta.supernote.note.plugincore.R.dimen.dp_px_56
        ));
        mEndHandleView.setStart(false);
        mEndHandleView.setSelListener(new SelHandleListener() {
            @Override
            public boolean onSelOffsetChange(int selOffset, boolean isBorder) {
                // 开始位置和结束位置不能相同，相同就不更新手柄
                if (selOffset == mSelStart) {
                    return false;
                }
                Log.i(TAG, "EndHandleView onSelOffsetChange:" + selOffset);
                mSelEnd = selOffset;
                isDraggingStart = mSelStart > mSelEnd;
                updateSystemSelOffset();
                updateSelHandleDrawable();
                mTextView.postInvalidate();

                return true;
            }

            @Override
            public void onDownHandle(boolean isDown) {
                isDownEndHandle = isDown;


            }

            @Override
            public void onClickHandle() {

            }

            @Override
            public void onTouchUp() {
                isDownEndHandle = false;
                isDraggingStart = false;
            }

            @Override
            public void onUpdateHandleMenuPosition(boolean isBorder) {
                updateSelectionMenuPosition(isBorder);

            }

        });
    }

    private void updateSelHandleDrawable() {
        if (mSelEnd > mSelStart) {
            if (!mStartHandleView.isStart) {
                mStartHandleView.setDrawable(
                        selStartBitmap);
                mStartHandleView.setStart(true);
                mStartHandleView.invalidate();
                mStartHandleView.update(false);
            }
            if (mEndHandleView.isStart) {
                mEndHandleView.setDrawable(selEndBitmap);
                mEndHandleView.setStart(false);
                mEndHandleView.invalidate();
                mEndHandleView.update(false);
            }

        } else {
            if (mStartHandleView.isStart) {
                mStartHandleView.setDrawable(
                        selEndBitmap);

                mStartHandleView.setStart(false);
                mStartHandleView.invalidate();
                mStartHandleView.update(false);
            }
            if (!mEndHandleView.isStart) {
                mEndHandleView.setDrawable(selStartBitmap);

                mEndHandleView.setStart(true);
                mEndHandleView.invalidate();
                mEndHandleView.update(false);
            }
        }
    }

    public void destroy() {
        hideCursorHandle();
        dismissPopupView();
        disposables.clear();
    }

    // 取消选中状态
    public void cancelSelection() {
        if (mTextView.isFocused()) {
            int selStart = Selection.getSelectionStart(mTextView.getText());
            Selection.setSelection((Spannable) mTextView.getText(), selStart);
        }
        Log.i(TAG, "cancelSelection");
        hideCursorHandle();
        dismissPopupView();

    }


    // 位置有变化，需要刷新界面
    public void refreshSelectionView() {
        if (mSelectionMenu.isShowing()) {
            showSelHandleView();
            updateSelectionMenuPosition();
        }
        // 刷新光标小水滴
        if (mCursorRect != null && mCursorHandleView.isShow()) {
            updateCursorPosition(new PointF(mCursorRect.cursorX, (mCursorRect.top + mCursorRect.bottom) / 2));
        }

    }

    // shezhiEditor的监听器
    public void setRattaEditorListener(RattaEditorListener rattaEditorListener) {
        this.mRattaEditorListener = rattaEditorListener;
    }

    public void setRattaEditorActionListener(RattaEditorActionListener mRattaEditorOpListener) {
        this.mRattaEditorActionListener = mRattaEditorOpListener;
    }

    // 更新原生的系统的选择坐标
    private void updateSystemSelOffset() {
        int selStart = Math.min(mSelStart, mSelEnd);
        int selEnd = Math.max(mSelStart, mSelEnd);
        int textLength = mTextView.getText().length();
        selStart = Math.max(0, Math.min(selStart, textLength));
        selEnd = Math.max(0, Math.min(selEnd, textLength));
        // 确保开始和结束位置不是一样的，方式设置光标失误
        if(selStart == selEnd) {
            dismissPopupView();
            return;
        }
        mTextView.requestFocus();
        Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
        isShowingSelectionMenu = false;
    }

    // 没有对光标拖柄操作，3s后隐藏手柄
    private void cursorHandleTimeout() {
        disposeCursorHandle();
        cursorHandleDisposable = Observable
                .just(1).delay(3, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Throwable {
                        isDownCursorHandle = false;
                        mCursorHandleView.dismiss();
                    }
                });
    }

    private void disposeCursorHandle() {
        if (cursorHandleDisposable != null && !cursorHandleDisposable.isDisposed()) {
            cursorHandleDisposable.dispose();
            cursorHandleDisposable = null;
        }
    }

    private void hideCursorHandle() {
        mCursorHandleView.dismiss();
        isDownCursorHandle = false;
        disposeCursorHandle();
    }

    private void showCursorHandleView(PointF pointF) {
        Log.i(TAG, "showCursorHandleView");
        mCursorHandleView.setSelOffset(mCursorIndex);
        boolean isBr = mCursorHandleView.isCursorHandleTail(pointF);
        mCursorHandleView.show(false, isBr);
        cursorHandleTimeout();

    }

    private void showSelHandleView() {
        // 避免出现两个拖柄
        if (mSelEnd == mSelStart) {
            dismissPopupView();
            return;
        }

        mEndHandleView.setSelOffset(mSelEnd);
        if (mEndHandleView.isStart) {
            mEndHandleView.setDrawable(selEndBitmap);
            mEndHandleView.invalidate();
            mEndHandleView.setStart(false);
        }
        mEndHandleView.show(false);
        mStartHandleView.setSelOffset(mSelStart);
        if (!mStartHandleView.isStart) {
            mStartHandleView.setDrawable(
                    selStartBitmap);
            mStartHandleView.invalidate();
            mStartHandleView.setStart(true);
        }
        mStartHandleView.show(false);
        mTextView.post(() -> {
            updateSystemSelOffset();


        });
        isDownEndHandle = isDownStartHandle = true;

    }

    // 获取光标位置菜单
    private int[] getCursorMenuPosition(int menuWidth, int menuHeight, boolean isShow) {

        // 获取光标小水滴的区域位置
        Rect cursorRect = mCursorHandleView.getHandleRect();

        int menuBottomMargin = mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_24);
        // 菜单水平离水滴的边距
        int menuHMargin = mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_40);
        // 屏幕宽度
        int screenWidth = getScreenWidth(mTextView.getContext());

        // 菜单放在水滴的右边
        int locationX = cursorRect.right + menuHMargin;

        // 菜单左边距大于屏幕宽度,需要将水滴放在左边
        if (locationX + menuWidth >= screenWidth - 10) {
            locationX = cursorRect.left - menuHMargin - menuWidth;
        }

        // 菜单放在当前行的下方
        int locationY = cursorRect.top + menuBottomMargin;

        // 获取TextView的滚动偏移量
        lastScrollY = mTextView.getScrollY();

        lastScrollX = mTextView.getScrollX();

        return new int[]{
                (int) (locationX),
                locationY
        };

    }


    // 获取选中菜单的位置
    private int[] getSelectionPosition(int menuWidth, int menuHeight, boolean isShow) {
        // 光标拖柄显示，说明需要显示光标菜单，显示逻辑不一样，需要另外计算
        if (mCursorHandleView.isShow()) {
            return getCursorMenuPosition(menuWidth, menuHeight, isShow);
        }
        lastScrollX = mTextView.getScrollX();
        // 获取TextView的滚动偏移量
        lastScrollY = mTextView.getScrollY();
        int selStart = Math.min(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
        int selEnd = Math.max(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
        Layout layout = mTextView.getLayout();

        int startLine = getSelStartLine();
        int endLine = getSelEndLine();
        boolean isEndBorder = isSelectionBr();
        // 如果show的时候获取位置，需要考虑边界问题
        /*if (isShow) {
            // 获取尾部上一个字符的行数
            int endPreLine = layout.getLineForOffset(selEnd - 1);
            // 尾部行号和尾部上一个字符的行号不一致，说明在边界
            isEndBorder = endLine != endPreLine;
            Log.i(TAG,"getSelectionPosition endLine:"+endLine+"=="+endPreLine);
            endLine = endPreLine;
        }*/
        int lineEndBottom = layout.getLineBottom(endLine);
        int lineEndTop = layout.getLineTop(endLine);

        Log.i(TAG,"getSelectionPosition startLine:"+startLine+", endLine:"+endLine);
        //
        int selLeft = (int) layout.getPrimaryHorizontal(selStart);
        Log.i(TAG,"getSelectionPosition selLeft:"+selLeft+"=="+layout.getLineLeft(startLine));
        int selRight = (int) (isEndBorder ?layout.getLineRight(endLine) : layout.getPrimaryHorizontal(selEnd));

        for (int line = startLine; line <= endLine; line++) {
            if (line != startLine) {
                // 获取左边界，要去最小的左边界
                selLeft = (int) Math.min(selLeft, layout.getLineLeft(line));
            }
            if (line == endLine && !isEndBorder) {
                selRight = (int) Math.max(selRight, layout.getPrimaryHorizontal(selEnd));
            } else {
                selRight = (int) Math.max(selRight, layout.getLineRight(line));
            }
        }
		Log.i(TAG,"getSelectionPosition startLine:"+startLine+", selLeft:"+selLeft);
        // 需要核对左右滚动的情况，左右滚动小水滴会超出输入框范围，菜单应该在输入框范围以内
        selLeft -= lastScrollX;
        selRight -= lastScrollX;

        if(selLeft < 0) {
            selLeft = 0;
        }
        if(selRight > mTextView.getWidth() - mTextView.getCompoundPaddingLeft() - mTextView.getCompoundPaddingRight()) {
            selRight = mTextView.getWidth() - mTextView.getCompoundPaddingLeft() - mTextView.getCompoundPaddingRight();
        }

        float selectionWidth = selRight - selLeft;


        int selectionWidthOffset = (int) ((selectionWidth - menuWidth) / 2);

        int[] location = new int[]{mPositionX, mPositionY};
//        mTextView.getLocationInWindow(location);

        // 获取结束位置的x轴坐标
        int xEnd = isEndBorder ? (int) layout.getLineRight(endLine) : (int) (layout.getPrimaryHorizontal(selEnd));
        // 相对屏幕的X轴坐标
        int locationEndX = location[0] + xEnd + mTextView.getPaddingLeft() - lastScrollX;
        int locationEndY = location[1] + layout.getLineBottom(endLine) + getTextViewPaddingTop() - lastScrollY;

        int locationStartX = (int) (location[0] + layout.getPrimaryHorizontal(selStart) + mTextView.getPaddingLeft() - lastScrollX);
        int locationStartY = location[1] + layout.getLineBottom(startLine) + getTextViewPaddingTop() - lastScrollY;

        int menuBottomMargin = mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_24);

        int locationX = location[0] + selectionWidthOffset + mTextView.getPaddingLeft() + selLeft;
        if (locationX < 10) {
            locationX = 10;
        }
        int screenWidth = getScreenWidth(mTextView.getContext());

        if (locationX + menuWidth > screenWidth - 10) {
            locationX = screenWidth - 10 - menuWidth;
        }
        int locationY = 0;

        /*// 尾标可见，需要计算
        if (isEndHandleVisible()) {

            locationY = location[1] + lineBottom + menuBottomMargin;

            /// 菜单下移分两种情况：
            /// 1.高亮区域宽度小于菜单宽度，需要加上小水滴的高度,宽度可能是负值，需要取绝对值
            /// 2.末尾水滴的x轴位置在菜单中间
            /// 3.末尾水滴加上宽度大于菜单x轴坐标，该情况出现在末尾水滴在行首的情况
            if (Math.abs(selectionWidth) < menuWidth
                    || (locationEndX > locationX && locationEndX < locationX + menuWidth)
                    || (locationEndX + mEndHandleView.getHandleWidth() > locationX && locationEndX + mEndHandleView.getHandleWidth() < locationX + menuWidth)) {
                locationY += mStartHandleView.getHandleHeight();
            }
            if (locationY < 10) {
                // 获取底部边线
                int endLineBottom = layout.getLineBottom(endLine);
                locationY = location[1] + endLineBottom + mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_80);
            }


            locationY = locationY - lastScrollY;
        } else {
            // 尾标不可见，放在文本框底部
            locationY = mTextView.getHeight() + location[1] + menuBottomMargin;
        }*/

        /*
         * 以下是计算选中菜单相对屏幕的y轴坐标值，分一下几种情况
         * 1.菜单在最后一行的底部
         * 2.如果最后一行的底部相对屏幕的Y轴坐标大于TextView的底部坐标，说明最后一行不可见，
         * 那么菜单应该放在TextView的底部边界的下方
         * 3.如果最后一行滑倒顶部上方，那么y轴坐标放在底部下方
         * 4.最后还需要核对菜单是否和两个小水滴相交，如果相交，那么菜单还需要下移一个小水滴高度的距离
         * 5.经过上面的核算，如果菜单与屏幕底部不可见，那么经放在屏幕底部的上方，往上移一个位置
         * */
        // 获取最后一行底部相对父布局窗口的的Y轴坐标值
        int lineEndBottomWindowY = location[1] + lineEndBottom + getTextViewPaddingTop() - lastScrollY;
        // 底部一样的top坐标，相对于父布局的windows，可能是屏幕，也可能是弹框
        int lineEndTopWindowY = location[1] + lineEndTop + getTextViewPaddingTop() - lastScrollY;
        Log.i(TAG,"getSelectionPosition location[1]:"+location[1]+"=lineEndBottom="+lineEndBottom+"=getTextViewPaddingTop="+getTextViewPaddingTop()
        +"=lastScrollY="+lastScrollY+"=getHeight="+mTextView.getHeight());
        // 最后一行Y轴坐标大于TextView底部坐标，菜单放在TexView底部
        if (lineEndBottomWindowY > location[1] + mTextView.getHeight()) {
            locationY = mTextView.getHeight() + location[1] + menuBottomMargin;
        } else if(lineEndBottomWindowY < location[1]){
            locationY = location[1] + menuBottomMargin;
        } else  {
            locationY = location[1] + lineEndBottom + menuBottomMargin + getTextViewPaddingTop() - lastScrollY;
        }


        Rect startRect = getStartHandleRect();
        Rect endRect = getEndHandleRect();
        Rect selectionRect = new Rect(locationX, locationY, locationX + menuWidth, locationY + menuHeight);
        Log.i(TAG, "getSelectionPosition startRect:" + startRect + "==" + endRect + "==" + selectionRect);


        // 可能和首位两个小水滴相交
        if ((startRect != null && Rect.intersects(startRect, selectionRect))
                || (endRect != null && Rect.intersects(endRect, selectionRect))) {
            locationY += mEndHandleView.getHandleHeight();
        }
        // 核算位置是否超过屏幕高度
        int screenHeight = getScreenHeight(mTextView.getContext());
        int yWindowsScreenOffset = mPositionYOnScreen - location[1];
        int locationYOnScreen = yWindowsScreenOffset + locationY;
        if (locationYOnScreen + menuHeight > screenHeight) {
            locationY = lineEndTopWindowY - menuHeight - menuBottomMargin;
        }

        return new int[]{
                locationX,
                locationY
        };
    }

    // 判断选中尾标是否可见
    private boolean isEndHandleVisible() {
        if (!mStartHandleView.isStart) {
            return mStartHandleView.getVisibility() == View.VISIBLE
                    && mStartHandleView.isShow();
        }
        if (!mEndHandleView.isStart) {
            return mEndHandleView.getVisibility() == View.VISIBLE
                    && mEndHandleView.isShow();
        }
        return false;
    }

    private boolean isStartHandleVisible() {
        if (mStartHandleView.isStart) {
            return mStartHandleView.getVisibility() == View.VISIBLE
                    && mStartHandleView.isShow();
        }
        if (mEndHandleView.isStart) {
            return mEndHandleView.getVisibility() == View.VISIBLE
                    && mEndHandleView.isShow();
        }
        return false;
    }

    private Rect getStartHandleRect() {
        if(mStartHandleView.isStart && mStartHandleView.getVisibility() == View.VISIBLE
                && mStartHandleView.isShow()) {
            return mStartHandleView.getHandleRect();
        }
        if(mEndHandleView.isStart && mEndHandleView.getVisibility() == View.VISIBLE
                && mEndHandleView.isShow()) {
            return mEndHandleView.getHandleRect();
        }
        return null;
    }

    private Rect getEndHandleRect() {
        if(!mStartHandleView.isStart && mStartHandleView.getVisibility() == View.VISIBLE
                && mStartHandleView.isShow()) {
            return mStartHandleView.getHandleRect();
        }
        if(!mEndHandleView.isStart && mEndHandleView.getVisibility() == View.VISIBLE
                && mEndHandleView.isShow()) {
            return mEndHandleView.getHandleRect();
        }
        return null;
    }

    public int getScreenWidth(Context context) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    public int getScreenHeight(Context context) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }


    // 添加粘贴的按钮
    private void addPasteView() {

        if (mTextView instanceof EditText) {

            // 是否有剪切板内容
            boolean isHasClip = checkClipboard();

            // 是否存在粘贴按钮
            boolean isExistPaste = false;
            int pasteIndex = -1;
            for (int i = 0; i < mSelectionView.getChildCount(); i++) {
                if (mSelectionView.getChildAt(i).getId() == pasteView.getId()) {
                    isExistPaste = true;
                    pasteIndex = i;
                    break;
                }
            }
            if (isExistPaste) {
                // 没有剪切内容删除粘贴按钮
                if (!isHasClip) {
                    mSelectionView.removeViewAt(pasteIndex);
                    // 删掉多余的分割线
                    mSelectionView.removeViewAt(pasteIndex);
                }
                return;
            }
            // 没有剪切板内容，不需要添加粘贴按钮
            if (!isHasClip) {
                return;
            }
            pasteIndex = 4;
            if (pasteIndex >= mSelectionView.getChildCount()) {
                pasteIndex = mSelectionView.getChildCount();
            }

            mSelectionView.addView(pasteView, pasteIndex);
            mSelectionView.addView(getMenuDivideView(), pasteIndex);
        }
    }

    private boolean isDownSelectionMenu(MotionEvent event) {
        return (event.getRawX() - lastSelectionMenuLocation[0]) < mSelectionView.getMeasuredWidth()
                && (event.getRawX() - lastSelectionMenuLocation[0]) > 0
                && (event.getRawY() - lastSelectionMenuLocation[1]) < mSelectionView.getMeasuredHeight()
                && (event.getRawY() - lastSelectionMenuLocation[1]) > 0;

    }

    int[] lastSelectionMenuLocation;

    // 显示选中菜单
    private void showSelectionMenu(boolean isPateMenu) {

        mSelectionView.removeAllViews();
        if (isPateMenu) {
            // 粘贴菜单，没有复制剪切,需要检查剪切板是否有内容，没有内容不需要粘贴
            if (checkClipboard()) {
                mSelectionView.addView(pasteView);
            } else {
                // 文本内容为空，有没有剪切内容，那就不需要菜单
                if (TextUtils.isEmpty(mTextView.getText())) {
                    return;
                }
            }
        } else {
            // 开始和结束相等的时候，不需要显示菜单，极限情况会出现这种问题
            if(mSelStart == mSelEnd || (mSelStart > mTextView.length() && mSelEnd > mTextView.length())) {
                return;
            }
            // 选中菜单，有复制剪切
            for (int i = 0; i < mBaseSelectMenuList.size(); i++) {
                mSelectionView.addView(mBaseSelectMenuList.get(i));
                if (i < mBaseSelectMenuList.size() - 1) {
                    View menuDivideView = getMenuDivideView();
                    mSelectionView.addView(menuDivideView);
                }
            }
            addPasteView();
        }


        updateSelectionMenuPosition(true);
        if (mRattaEditorListener != null) {
            mRattaEditorListener.onShowSelection(true, lastSelectionMenuLocation, mSelectionView.getMeasuredWidth(), mSelectionView.getMeasuredHeight());
        }
    }

    private void updateSelectionMenuPosition() {
        updateSelectionMenuPosition(false);
    }

    private void updateSelectionMenuPosition(boolean isShow) {
        isDownSelectionMenu = true;
        int textLength = mTextView.getText().length();

        // 是否全选了文字
        boolean isSelectedAll = mSelStart == 0 && mSelEnd == textLength
                && textLength > 0;
        View endView = mSelectionView.getChildAt(mSelectionView.getChildCount() - 1);
        if (isSelectedAll) {
            if (endView == null || endView.getId() == selectAllView.getId()) {
                mSelectionView.removeView(endView);
                mSelectionView.removeViewAt(mSelectionView.getChildCount() - 1);
            }
        } else if (textLength > 0) {
            if (endView == null || endView.getId() != selectAllView.getId()) {
                mSelectionView.addView(getMenuDivideView());
                mSelectionView.addView(selectAllView);
            }
        }
        mSelectionView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));


        // 获取精确选择坐标
        int[] pos = getSelectionPosition(mSelectionView.getMeasuredWidth(),
                mTextView.getContext().getResources().getDimensionPixelSize(com.ratta.supernote.note.plugincore.R.dimen.dp_px_64), isShow);
        lastSelectionMenuLocation = pos;
        // 显示菜单（带防闪动检测）
        if (!mSelectionMenu.isShowing()) {
            mSelectionMenu.showAtLocation(mTextView, Gravity.START | Gravity.TOP, pos[0], pos[1]);
        } else {
            mSelectionMenu.update(pos[0], pos[1], -1, -1);
        }
    }

    private void dismissPopupView() {
        Log.i(TAG, "dismissPopupView");

//        mSelEnd = mSelStart = 0;
        mStartHandleView.dismiss();
        mEndHandleView.dismiss();
        if (!mCursorHandleView.isShow()) {
            dismissSelectionMenu();
        }

    }

    private void dismissSelectionMenu() {
        Log.i(TAG, "dismissSelectionMenu");
        if (mSelectionMenu.isShowing()) {
            mSelectionMenu.dismiss();
            if (mRattaEditorListener != null) {
                mRattaEditorListener.onShowSelection(false, lastSelectionMenuLocation, mSelectionView.getMeasuredWidth(), mSelectionView.getMeasuredHeight());
            }
        }
    }


    public void setCursorRect(CursorRect cursorRect) {
        this.mCursorRect = cursorRect;
    }

    // 光标是否有变动
    boolean isChangeCursor;

    // 获取光标所在文字的位置
    private int getOffsetForPosition(PointF pointF) {
        Layout layout = mTextView.getLayout();
        int position = mTextView.getOffsetForPosition(pointF.x, pointF.y);
        if (position == -1) return position;
        if (TextUtils.isEmpty(mTextView.getText())) {
            return position;
        }
        // 行号大于1,且下一位的位置小于等于总长度需要确认队尾和队首
        // 因为队尾光标定位不到，总会跳到上个字符，需要特殊处理
        if (layout.getLineCount() > 1 && position + 1 <= mTextView.getText().length()) {
            int nextPositionLine = layout.getLineForOffset(position + 1);
            int positionLine = layout.getLineForOffset(position);
            // 下一个位置的行号对不上当前行号，需要确认是否要绘制队尾光标
            if (nextPositionLine != positionLine && !isPositionContainsTab(position)) {
                float positionX = (layout.getPrimaryHorizontal(position) + mTextView.getCompoundPaddingLeft());
                float lineRight = layout.getLineRight(positionLine) + mTextView.getCompoundPaddingLeft();
                if (pointF.x - positionX > lineRight - pointF.x) {
                    position += 1;
                }
            }
        }
        return position;

    }

    // 偏移位置附件是否有换行符
    public boolean isPositionContainsTab(int position) {
        String contentStr = mTextView.getText().toString();
        if (TextUtils.isEmpty(contentStr)) {
            return false;
        }
        String subStr;
        if (position >= contentStr.length()) {
            subStr = contentStr.substring(contentStr.length() - 1);

        } else {
            int start = Math.max(0, position - 1);
            subStr = contentStr.substring(start, position + 1);
        }
        return subStr.contains("\n") || subStr.contains("\r");
    }

    private void updateCursorPosition(PointF pointF) {
        if (mCursorHandleView.isShow()) {
            showCursorHandleView(pointF);
            isDownStartHandle = isDownEndHandle = false;
        }
    }

    // 处理单击光标事件
    private void handleCursorClickEvent(MotionEvent event) {
        Log.i(TAG,"handleCursorClickEvent event:"+event.toString());
        // 选中状态，处理
        /*if(mSelEnd != mSelStart) {
            return;
        }*/
        // 获取触摸点对应的字符偏移
        int offset = getOffsetForPosition(new PointF(event.getX(), event.getY()));
        Log.i(TAG, "handleCursorClickEvent offset:"+offset+"=="+mCursorRect);

        if (offset == -1) return;
        if (mCursorRect == null) {
            return;
        }


        // 只有EditText才有光标点击事件
        if (mTextView instanceof EditText) {
            if (TextUtils.isEmpty(mTextView.getText())) {
                if (mSelectionMenu.isShowing()) {
                    mSelectionMenu.dismiss();
                } else {
                    if(cursorMenuEnabled) {
                        showSelectionMenu(true);
                    }
                }
            } else {
                dismissPopupView();
                int cursorPosition = Selection.getSelectionStart(mTextView.getText());

                mSelStart = mSelEnd = mCursorIndex = offset;
//                showSelectionMenu(true);

                dismissSelectionMenu();
                isChangeCursor = false;
                if (offset != cursorPosition) {
                    Selection.setSelection(((EditText) mTextView).getText(), offset);
                }
                // 相等显示拖柄

                showCursorHandleView(new PointF(event.getX(), event.getY()));
                isDownStartHandle = isDownEndHandle = false;
                mTextView.postInvalidate();
            }


//            Log.i(TAG, "handleLongPress text length"+content.length()+"=="+cursorPosition);
//            Log.i(TAG,"handleLongPress cursorX:"+ mCursorRect.cursorX+"=="+ mCursorRect.top+"=="+ mCursorRect.bottom+"=="+event.getX()+"=="+event.getY()+"=="+Math.abs(mCursorRect.cursorX - event.getX()) );
//
//
//            // 需要考虑滑动的事件，左对齐居中会有x轴的滑动偏移
//            if (TextUtils.isEmpty(content)
//                    || (((content.length() == cursorPosition && event.getX() >= mCursorRect.cursorX)
//                    || Math.abs(mCursorRect.cursorX - mTextView.getScrollX() - event.getX()) <= 10)
//                    && event.getY() > mCursorRect.top - mTextView.getScrollY()
//                    && event.getY() < mCursorRect.bottom - mTextView.getScrollY())
//            ) {
//                mSelStart = mSelEnd = mCursorIndex = cursorPosition;
////                showSelectionMenu(true);
//                // 只需要显示拖柄
//                showCursorHandleView();
//                isDownStartHandle = isDownEndHandle = false;
//                mTextView.invalidate();
//                isShowingSelectionMenu = false;
//                return;
//            } else {
//                hideCursorHandle();
//            }
        }
        isShowingSelectionMenu = false;

    }

    // 是否在选中界面显示中
    boolean isShowingSelectionMenu = false;

    // 处理选中点击事件
    private void handleSelectClickEvent(MotionEvent event) {

        // 获取触摸点对应的字符偏移
        int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
        if (offset == -1) return;
        // 内容为空，不需要弹菜单
        if (TextUtils.isEmpty(mTextView.getText())) {
            return;
        }
        isShowingSelectionMenu = true;
        // 触发重绘
//        if (mTextView instanceof EditText) {
//            Layout layout = mTextView.getLayout();
//            String content = mTextView.getText().toString();
//            int cursorPosition = Selection.getSelectionStart(mTextView.getText());
//
//            int cursorLine = layout.getLineForOffset(cursorPosition);
//            float lineTop = layout.getLineTop(cursorLine) + getTextViewPaddingTop();
//            float lineBottom = layout.getLineBottom(cursorLine) + getTextViewPaddingTop();
//            Log.i(TAG, "handleLongPress text length"+content.length()+"=="+cursorPosition);
//            Log.i(TAG,"handleLongPress cursorX:"+cursorX+"=="+ lineTop+"=="+lineBottom+"=="+event.getX()+"=="+event.getY()+"=="+Math.abs(cursorX - event.getX()) );
//
//            if (TextUtils.isEmpty(content)
//                    || (content.length() == cursorPosition && event.getX()> cursorX || event.getY() > lineBottom)
//                    || (Math.abs(cursorX - event.getX()) <= 10 && event.getY() > lineTop
//                    && event.getY() < lineBottom)) {
//                mSelStart = mSelEnd = cursorPosition;
//                showSelectionMenu(true);
//                isDownStartHandle = isDownEndHandle = false;
//                mTextView.invalidate();
//                isShowingSelectionMenu = false;
//                return;
//            }
//        }

        hideCursorHandle();
        // 找到单词边界
        int[] wordRange = getWordBoundaries(offset);
        mSelStart = wordRange[0];
        mSelEnd = wordRange[1];
        Log.i(TAG, "handleLongPress mSelStart:" + mSelStart + ", mSelEnd" + mSelEnd);
        // 显示拖柄
        showSelHandleView();
        showSelectionMenu(false);
        // 需要放在主线程更新，直接刷新会出现显示问题
        mTextView.postInvalidate();

    }

    private void updateSelectionPopWindows() {
        hideCursorHandle();
        mStartHandleView.setSelOffset(mSelStart);
        mEndHandleView.setSelOffset(mSelEnd);
        updateSelHandleDrawable();
        mStartHandleView.update(false);
        mEndHandleView.update(false);
        showSelectionMenu(false);

    }

    // 获取单词的起始和结束位置
    private int[] getWordBoundaries(int offset) {
        String text = mTextView.getText().toString();
        BreakIterator iterator = BreakIterator.getWordInstance(Locale.getDefault());
        iterator.setText(text);

        int start = iterator.preceding(offset);
        int end = iterator.following(offset);

        // 处理边界情况
        if (start == BreakIterator.DONE) start = 0;
        if (end == BreakIterator.DONE) end = text.length();

        return new int[]{start, end};
    }

    public int getSelStart() {
        return mSelStart;
    }

    public int getSelEnd() {
        return mSelEnd;
    }

    private Path getUpdatedHighlightPath() {
        Path highlight = null;
        Layout layout = mTextView.getLayout();
        int selStart = Math.min(Selection.getSelectionStart(mTextView.getText()), Selection.getSelectionEnd(mTextView.getText()));
        int selEnd = Math.max(Selection.getSelectionStart(mTextView.getText()), Selection.getSelectionEnd(mTextView.getText()));
        if (selStart >= 0 && selEnd >= 0) {
            if (selStart == selEnd) {
                return null;
            } else {

                highlight = new Path();


                float spacingExtra = mTextView.getLineSpacingExtra(); // 行间距值，单位像素（根据需求调整）
                int startLine = layout.getLineForOffset(selStart);
                int endLine = layout.getLineForOffset(selEnd);
                // 获取尾部上一个字符的行数
                int endPreLine = layout.getLineForOffset(selEnd - 1);
                // 尾部行号和尾部上一个字符的行号不一致，说明在边界
                boolean isEndBorder = endLine != endPreLine;
                if (isEndBorder) {
                    // 边界会换一行，所以需要-1，
                    endLine -= 1;
                }
                // 获取最后一行
                int finalLine = layout.getLineForOffset(mTextView.getText().length());
                // 获取EditText的内边距
                int paddingLeft = mTextView.getPaddingLeft();
                int paddingTop = getTextViewPaddingTop();
                // 字体倾斜偏移量
                float skewOffset = getSkewOffset();

                TextPaint paint = layout.getPaint();

                String contentStr = mTextView.getText().toString();
                for (int line = startLine; line <= endLine; line++) {
                    int lineStart = layout.getLineStart(line);
                    int lineEnd = layout.getLineEnd(line);

                    if(selStart <= lineStart && selEnd >= lineEnd) {
                        HighLightLineInfo textLineInfo = mHighLightMap.get(line);
                        if (textLineInfo == null) {
                            textLineInfo = getHighLightInfo(contentStr, layout,
                                    line, finalLine,paddingLeft, paddingTop, spacingExtra);
                            mHighLightMap.put(line, textLineInfo);
                        }
                        highlight.addPath(textLineInfo.linePath);
                        continue;
                    }


                    boolean isRtlParagraph = layout.getParagraphDirection(line) == -1;
                    float lineTop = layout.getLineTop(line) + paddingTop;

                    float lineBottom = layout.getLineBottom(line) + paddingTop;


                    // 计算当前行的选中范围
                    int lineSelStart = Math.max(selStart, lineStart);
                    int lineSelEnd = Math.min(selEnd, lineEnd);


                    // 调整顶部和底部间距,间距可能还需要调整
                    float adjustedTop = lineTop;
                    // 文本的最后一行不需要减去行间距
                    float adjustedBottom = (line == finalLine ? lineBottom : lineBottom - spacingExtra);

                    // 判断该行是否只有一个换行符
                    boolean isLF = false;
                    if (lineEnd - lineStart == 1
                            && (mTextView.getText().charAt(lineStart) == '\n'
                            || mTextView.getText().charAt(lineStart) == '\r')) {
                        isLF = true;
                    }
                    RectF lastRectF = null;
                    if (lineSelStart < lineSelEnd) {


                        for (int offset = lineSelStart; offset < lineSelEnd; ) {
                            // 边界检查，防止越界
                            if (offset >= contentStr.length()) {
                                break;
                            }

                            int codePoint = contentStr.codePointAt(offset);
                            int charCount = Character.charCount(codePoint);

                            // 确保不会越界
                            int endOffset = Math.min(offset + charCount, lineEnd);
                            if (endOffset > contentStr.length()) {
                                endOffset = contentStr.length();
                            }

                            // 获取当前字符（或字符组合）的字符串
                            String currentChar = contentStr.substring(offset, endOffset);
                            // 获取当前下标和上一次下标的文字宽度
                            float textWidth = 0;
                            if(TextUtils.equals(currentChar, "\t")) {
                                // 使用Layout的方法获取准确的制表符宽度
                                float startPos = layout.getPrimaryHorizontal(offset);
                                float endPos;
                                if (offset + 1 < contentStr.length()) {
                                    endPos = layout.getPrimaryHorizontal(offset + 1);
                                } else {
                                    // 如果是最后一个字符，使用行尾位置
                                    endPos = layout.getLineRight(layout.getLineForOffset(offset));
                                }
                                textWidth = endPos - startPos;
                            } else {
                                textWidth = paint.measureText(currentChar);
                            }


                            boolean isRtlChar = layout.isRtlCharAt(offset);
                            float startX = 0;
                            if(isRtlParagraph) {
                                if(lineEnd - lineStart > 1
                                        && (TextUtils.equals(currentChar, "\n")
                                        || TextUtils.equals(currentChar, "\r"))) {
                                    break;
                                }
                                startX = isRtlChar ? layout.getPrimaryHorizontal(offset) - textWidth
                                        : layout.getSecondaryHorizontal(offset);
                            } else {
                                startX = !isRtlChar ? layout.getPrimaryHorizontal(offset)
                                        : layout.getSecondaryHorizontal(offset) - textWidth;
                                /// 需要对该字符的上一个字符进行核对
                                /// 如果该字符的上一个字符是希伯来语，那么在该字符是数字的时候，上面逻辑获取到的位置值是会和上一个希伯来语字符重叠
                                /// 这是Android本身bug，需要通过getSecondaryHorizontal获取位置，这个位置才是正常的。
                                if (offset > 0 && !isRtlChar) {
                                    boolean isRtlNextChar = layout.isRtlCharAt(offset - 1);
                                    if (isRtlNextChar) {
                                        float preCharWidth = paint.measureText(String.valueOf(contentStr.charAt(offset - 1)));
                                        float preX = layout.getSecondaryHorizontal(offset - 1) - preCharWidth;
                                        // 字符重叠了
                                        if (Math.abs(preX - startX) < textWidth / 2) {
                                            startX = layout.getSecondaryHorizontal(offset);
                                        }

                                    }
                                }
                            }
                            startX += mTextView.getCompoundPaddingLeft();

                            float endX = startX + textWidth + skewOffset;
                            if (TextUtils.equals(currentChar, "\n") || TextUtils.equals(currentChar, "\r")) {
                                endX = startX + getLFWidth();
                            }

                            // 获取字符的边界
                            Rect bounds = new Rect();
                            mTextView.getPaint().getTextBounds(currentChar, 0, currentChar.length(), bounds);
                            if (bounds.left < 0) {
                                // 需要加上字符边距，否则会覆盖不上选中字符
                                startX += bounds.left;
                            }
                            RectF rectF = new RectF(
                                    startX,
                                    adjustedTop,
                                    endX,
                                    adjustedBottom);
                            // 字符之间可能有缝隙，需要判断上一个rect的右坐标和当前rect的左坐标差是否需要2px
                            if (lastRectF != null && Math.abs(rectF.left - lastRectF.right) <= 2) {
                                rectF.left = lastRectF.right;

                            }
                            lastRectF = rectF;

                            highlight.addRect(rectF,
                                    Path.Direction.CW);
                            // 移动到下一个字符
                            offset += charCount;


                        }
                    }


                }

                mHighlightPaint.setColor(Color.BLACK);
                mHighlightPaint.setStyle(Paint.Style.FILL);
            }
        }
        return highlight;
    }


    /**
     * 获取每一行的高亮区域
     * @param layout
     * @param selStart
     * @param selEnd
     * @param line
     * @param startLine
     * @param endLine
     * @param finalLine
     * @param isEndBorder
     * @return
     */
    private RectF getLineHighlightRect(Layout layout, int selStart, int selEnd, int line, int startLine, int endLine,
                                       int finalLine, boolean isEndBorder) {
        int paddingLeft = mTextView.getPaddingLeft();
        int paddingTop = getTextViewPaddingTop();
        float spacingExtra = mTextView.getLineSpacingExtra(); // 行间距值，单位像素（根据需求调整）

        float lineTop = layout.getLineTop(line) + paddingTop;

        float lineBottom = layout.getLineBottom(line) + paddingTop;
        int lineStart = layout.getLineStart(line);
        int lineEnd = layout.getLineEnd(line);

        // 计算当前行的选中范围
        int lineSelStart = Math.max(selStart, lineStart);
        int lineSelEnd = Math.min(selEnd, lineEnd);

        // 调整顶部和底部间距,间距可能还需要调整
        float adjustedTop = lineTop;
        // 文本的最后一行不需要减去行间距
        float adjustedBottom = line == finalLine ? lineBottom : lineBottom - spacingExtra;

        // 获取当前行的左右边界
        float left = line == startLine ?
                layout.getPrimaryHorizontal(selStart) :
                layout.getLineLeft(line);
        left = left + paddingLeft;

        // 考虑滚动位置
        int scrollX = mTextView.getScrollX();

        // 确保左边界不小于paddingLeft（考虑滚动）
//        left = Math.max(left, paddingLeft + scrollX);

        float right;
        if (line == endLine && !isEndBorder) {
            right = layout.getPrimaryHorizontal(selEnd) + paddingLeft;
        } else {
            right = layout.getLineRight(line) + paddingLeft;
        }
        right += getSkewOffset();
        // 确保右边界不超过TextView的宽度减去右padding
//        right = Math.min(right, mTextView.getWidth() - mTextView.getCompoundPaddingRight() + scrollX);
        //判断该行是否只有一个换行符
        if (lineSelEnd - lineSelStart == 1
                && (mTextView.getText().charAt(lineSelStart) == '\n'
                || mTextView.getText().charAt(lineSelStart) == '\r')) {
            right = left + getLFWidth();
        }



        // 添加调试日志
        Log.i(TAG, "高亮区域计算 - 行:" + line +
                " paddingLeft:" + paddingLeft +
                " paddingTop:" + paddingTop +
                " left:" + left +
                " right:" + right);


        RectF rect = new RectF(
                left,
                adjustedTop,
                right,
                adjustedBottom
        );
        return rect;
    }

    /**
     * 获取每一行的高亮区域,改行是从右到左显示的，方式不一样
     * @param layout
     * @param selStart
     * @param selEnd
     * @param line
     * @param startLine
     * @param endLine
     * @param finalLine
     * @param isEndBorder
     * @return
     */
    private void getLineRtlHighlightRect(Layout layout, int selStart, int selEnd, int line, int startLine, int endLine,
                                         int finalLine, Path highlight, boolean isEndBorder) {
        TextPaint paint = mTextView.getPaint();
        int paddingLeft = mTextView.getCompoundPaddingLeft();
        int paddingTop = getTextViewPaddingTop();
        float spacingExtra = mTextView.getLineSpacingExtra(); // 行间距值，单位像素（根据需求调整）

        // 字体倾斜偏移量
        float skewOffset = getSkewOffset();

        float lineTop = layout.getLineTop(line) + paddingTop;

        float lineBottom = layout.getLineBottom(line) + paddingTop;

        // 调整顶部和底部间距,间距可能还需要调整
        float adjustedTop = lineTop;
        // 文本的最后一行不需要减去行间距
        float adjustedBottom = line == finalLine ? lineBottom : lineBottom - spacingExtra;

        int lineStart = selStart;
        if (line != startLine) {
            lineStart = layout.getLineStart(line);
        }
        int lineEnd = selEnd;
        if (line != endLine) {
            lineEnd = layout.getLineEnd(line);
        }

        // 判断该行是否只有一个换行符
        boolean isLF = false;
        if (lineEnd - lineStart == 1
                && (mTextView.getText().charAt(lineStart) == '\n'
                || mTextView.getText().charAt(lineStart) == '\r')) {
            isLF = true;
        }
        if (lineStart < lineEnd) {
            String contentStr = mTextView.getText().toString();

            for (int offset = lineStart; offset < lineEnd; offset++) {
                float startX = (layout.isRtlCharAt(offset) ? layout.getPrimaryHorizontal(offset + 1)
                        : layout.getSecondaryHorizontal(offset)) + paddingLeft;
                String str = contentStr.substring(offset, offset + 1);
                // 获取当前下标和上一次下标的文字宽度
                float textWidth = paint.measureText(str);
                float endX = startX + textWidth + skewOffset;
                if ((TextUtils.equals(str, "\n") || TextUtils.equals(str, "\r")) && !isLF) {
                    continue;
                }
                if (isLF) {
                    endX = startX + getLFWidth();
                }
                highlight.addRect(new RectF(
                                startX,
                                adjustedTop,
                                endX,
                                adjustedBottom),
                        Path.Direction.CW);


            }
        }
    }

    // 获取字体的倾斜距离，需要加上
    public float getSkewOffset() {
       /* boolean isItalic = mTextView.getTypeface() != null &&
                (mTextView.getTypeface().getStyle() & Typeface.ITALIC) != 0;
        if (isItalic) {
            float textSize = mTextView.getTextSize();
            float italicAngle = -0.25f;
            float skewOffset = textSize * Math.abs(italicAngle) * 0.5f;
            return skewOffset;
        }*/
        float textSize = mTextView.getTextSize();
        float italicAngle = mTextView.getPaint().getTextSkewX();
        float skewOffset = textSize * Math.abs(italicAngle) * 0.5f;
        return skewOffset;
    }

    // 获取换行符宽度
    public float getLFWidth() {
        float textSize = mTextView.getTextSize();
        return textSize * 0.25f * 0.5f;
    }

    /**
     * 校正光标位置,因为点不到最后的位置
     */
    private void adjustCursorPosition(PointF pointF) {
        Layout layout = mTextView.getLayout();
        if (layout == null) {
            return;
        }
        Log.i(TAG,"adjustCursorPosition pointF:"+pointF.toString());
        // 获取光标的位置,返回的是位置所在的位置，不是坐标值
        int cursorPosition = mTextView.getSelectionStart();
        // 这个返回的是实际坐标值，相对文本框的x坐标
        float cursorX = (int) (layout.getPrimaryHorizontal(cursorPosition) + mTextView.getCompoundPaddingLeft());
        // 获取当前行号
        int cursorLine = layout.getLineForOffset(cursorPosition);
        lastDownPoint = pointF;
        // 行号大于1,且下一位的位置小于等于总长度需要确认队尾和队首
        // 因为队尾光标定位不到，总会跳到上个字符，需要特殊处理
        if(pointF != null &&  layout.getLineCount() >1 && cursorPosition + 1 <= mTextView.getText().length()) {
            int nextPositionLine = layout.getLineForOffset(cursorPosition + 1);
            // 下一个位置的行号对不上当前行号，需要确认是否要绘制队尾光标
            if(nextPositionLine != cursorLine && !isPositionContainsTab(cursorPosition)) {
                float lineRight = layout.getLineRight(cursorLine);
                // 点击位置靠近右侧，光标要在队尾
                if(pointF.x - cursorX > lineRight - pointF.x) {
                    isCursorTail = true;
                    //点击队尾会显示上一个字符的光标，所以要+1
                    if(mTextView instanceof EditText) {
                        ((EditText)mTextView).setSelection(cursorPosition + 1);
                    } else {
                        Selection.setSelection(((EditText) mTextView).getText(), cursorPosition + 1);
                    }

                }
            } else if(isCursorTail) {
                // 上一次记录的光标在队尾，需要进行判断是否点到了队首，因为原生EditText只能点到队首，点不到队尾
                float lineTop = layout.getLineTop(cursorLine) + getTextViewPaddingTop() - mTextView.getScrollY();
                float lineBottom = layout.getLineBottom(cursorLine) + getTextViewPaddingTop() - mTextView.getScrollY();
                if(pointF.y > lineTop && pointF.y < lineBottom) {
                    mTextView.postInvalidate();
                }
            }

        }

    }


    public boolean onTouchEvent(MotionEvent event) {

        action = event.getAction();
        if(action == MotionEvent.ACTION_DOWN) {
            lastDownPoint = new PointF(event.getX(), event.getY());
            adjustCursorPosition(lastDownPoint);
        }
        if(onDoubleTapEvent(event)) {
            return true;
        }

        mGestureDetector.onTouchEvent(event);

        return false;
    }

    private long lastTapTime = 0;
    private float lastTapX = 0;
    private float lastTapY = 0;
    private static final int DOUBLE_TAP_TIMEOUT = 300;
    boolean isDoubleTap = false;

    // 出阿里双击事件
    public boolean onDoubleTapEvent(MotionEvent event) {
        // 处理双击事件
        if (event.getAction() == MotionEvent.ACTION_DOWN) {

            long currentTime = System.currentTimeMillis();
            float x = event.getX();
            float y = event.getY();

            // 检测是否是双击（时间间隔小于300ms且位置接近）
            if (selectionMenuEnabled && currentTime - lastTapTime < DOUBLE_TAP_TIMEOUT &&
                    Math.abs(x - lastTapX) < 30 &&
                    Math.abs(y - lastTapY) < 30) {

                // 是双击事件，重置计时器并拦截系统默认处理
                lastTapTime = 0;

                // 如果需要自定义双击行为，可以在这里添加
                // 例如：选择当前单词
                // int offset = getOffsetForPosition(x, y);
                // selectWordAt(offset);
                isDoubleTap = true;
                // 返回true表示已处理此事件
                handleSelectClickEvent(event);
                return true;
            }


            // 记录本次点击
            lastTapTime = currentTime;
            lastTapX = x;
            lastTapY = y;
        } else if (event.getAction() == MotionEvent.ACTION_CANCEL
                || event.getAction() == MotionEvent.ACTION_UP) {
            float currentX = event.getX();
            float currentY = event.getY();
            if(mCursorHandleView.isShow() && mSelectionMenu.isShowing()) {
                dismissSelectionMenu();
            }
            // 防止
            hideCursorHandle();

            // 判断是否是单击事件
            if (!isDoubleTap && System.currentTimeMillis() - lastTapTime < 300
                    && Math.abs(currentX - lastTapX) < 30
                    && Math.abs(currentY - lastTapY) < 30) {
                // 处理单击事件，如果点击光标，需要显示拖柄
                handleCursorClickEvent(event);
            }
            isDoubleTap = false;
        }
        return false;
    }

    public void drawSelectedText(Canvas canvas) {
        if (!mTextView.isFocused()) {
//            cancelSelection();
            return;
        }
        int selStart = Math.min(Selection.getSelectionStart(mTextView.getText()), Selection.getSelectionEnd(mTextView.getText()));
        int selEnd = Math.max(Selection.getSelectionStart(mTextView.getText()), Selection.getSelectionEnd(mTextView.getText()));
        TextPaint paint = mTextView.getPaint();


        // 如果有选中的文本，重新绘制选中部分
        if (selStart != selEnd) {
            paint.setColor(Color.WHITE);
            Layout layout = mTextView.getLayout();
            if (layout != null) {
                int startLine = layout.getLineForOffset(selStart);
                int endLine = layout.getLineForOffset(selEnd);
                // 获取尾部上一个字符的行数
                int endPreLine = layout.getLineForOffset(selEnd - 1);
                // 尾部行号和尾部上一个字符的行号不一致，说明在边界
                boolean isEndBorder = endLine != endPreLine;
                if (isEndBorder) {
                    // 边界会换一行，所以需要-1，
                    endLine -= 1;
                }
                // 字体倾斜偏移量
                float skewOffset = getSkewOffset();
                float spacingExtra = mTextView.getLineSpacingExtra(); // 行间距值，单位像素（根据需求调整）
                int finalLine = layout.getLineForOffset(mTextView.getText().length());

                String contentStr = mTextView.getText().toString();
                char[] contentChars = contentStr.toCharArray();
                canvas.save();
                // 设置裁剪区域，排除padding区域
                int paddingLeft = mTextView.getCompoundPaddingLeft();
                int paddingTop = getTextViewPaddingTop();
                int paddingRight = mTextView.getCompoundPaddingRight();
                int paddingBottom = mTextView.getCompoundPaddingBottom();

                // 创建裁剪区域，只在内容区域绘制
                canvas.clipRect(
                        paddingLeft + mTextView.getScrollX(),
                        paddingTop + mTextView.getScrollY(),
                        mTextView.getWidth() - paddingRight + mTextView.getScrollX(),
                        mTextView.getHeight() - paddingBottom + mTextView.getScrollY()
                );

                canvas.translate(mTextView.getCompoundPaddingLeft(), getTextViewPaddingTop());

                // 处理多行文本
                for (int line = startLine; line <= endLine; line++) {
                    // 获取当前行的文字书写方式，从左到右又是从右到左
                    int lineStart = layout.getLineStart(line);
                    int lineEnd = layout.getLineEnd(line);
                    String lineStr = contentStr.substring(lineStart, lineEnd);

                    if(selStart <= lineStart && lineEnd <= selEnd && !lineStr.contains("\t")) {

                        HighLightLineInfo textLineInfo = mHighLightMap.get(line);
                        if (textLineInfo == null || textLineInfo.start != lineStart || textLineInfo.end != lineEnd) {
                            textLineInfo = getHighLightInfo(contentStr, layout,
                                    line, finalLine, paddingLeft, paddingTop, spacingExtra);
                            mHighLightMap.put(line, textLineInfo);
                        }

                        canvas.drawPath(textLineInfo.linePath, mHighlightPaint);

                        canvas.drawText(contentStr,textLineInfo.start, textLineInfo.end, textLineInfo.x, textLineInfo.y, paint);
                        continue;
                    }

                    // 获取当前行的文字书写方式，从左到右又是从右到左
                    boolean isRtlParagraph = layout.getParagraphDirection(line) == -1;

                    // 计算当前行的选中范围
                    int lineSelStart = Math.max(selStart, lineStart);
                    int lineSelEnd = Math.min(selEnd, lineEnd);

                    RectF lastRectF = null;
                    // 如果当前行有选中的文本
                    if (lineSelStart < lineSelEnd) {
                        float lineTop = layout.getLineTop(line);

                        float lineBottom = layout.getLineBottom(line);
                        // 调整顶部和底部间距,间距可能还需要调整
                        float adjustedTop = lineTop;
                        // 文本的最后一行不需要减去行间距
                        float adjustedBottom = (line == finalLine ? lineBottom : lineBottom - spacingExtra);

                        float startX;
                        float baseline = layout.getLineBaseline(line);

                        for (int offset = lineSelStart; offset < lineSelEnd;) {
                            // 边界检查，防止越界
                            if (offset >= contentStr.length()) {
                                break;
                            }
                            int codePoint = contentStr.codePointAt(offset);
                            int charCount = Character.charCount(codePoint);
                            // 确保不会越界
                            int endOffset = Math.min(offset + charCount, lineEnd);
                            if (endOffset > contentStr.length()) {
                                endOffset = contentStr.length();
                            }

                            // 获取当前字符（或字符组合）的字符串
                            String currentChar = contentStr.substring(offset, endOffset);
                            // 获取当前字符的字宽
                            float charWidth = paint.measureText(currentChar);
                            if(TextUtils.equals(currentChar, "\t")) {
                                // 使用Layout的方法获取准确的制表符宽度
                                float startPos = layout.getPrimaryHorizontal(offset);
                                float endPos;
                                if (offset + 1 < contentStr.length()) {
                                    endPos = layout.getPrimaryHorizontal(offset + 1);
                                } else {
                                    // 如果是最后一个字符，使用行尾位置
                                    endPos = layout.getLineRight(layout.getLineForOffset(offset));
                                }
                                charWidth = endPos - startPos;
                            }
                            // 需要判断文字的书写方式和行的书写方式是否一直，如果一致，通过getPrimaryHorizontal获取偏移量，不一致，通过getSecondaryHorizontal获取偏移量
                            boolean isRtlChar = layout.isRtlCharAt(offset);
                            if (isRtlParagraph) {
                                if(TextUtils.equals(currentChar, "\n") || TextUtils.equals(currentChar, "\r")) {
                                    offset += charCount;
                                    continue;
                                }
                                startX = isRtlChar ? layout.getPrimaryHorizontal(offset) - charWidth
                                        : layout.getSecondaryHorizontal(offset);
                            } else {
                                startX = !isRtlChar ? layout.getPrimaryHorizontal(offset)
                                        : layout.getSecondaryHorizontal(offset) - charWidth;
                                /// 需要对该字符的上一个字符进行核对
                                /// 如果该字符的上一个字符是希伯来语，那么在该字符是数字的时候，上面逻辑获取到的位置值是会和上一个希伯来语字符重叠
                                /// 这是Android本身bug，需要通过getSecondaryHorizontal获取位置，这个位置才是正常的。
                                if(offset > 0 && !isRtlChar) {
                                    boolean isRtlNextChar = layout.isRtlCharAt(offset - 1);
                                    if(isRtlNextChar) {
                                        float preCharWidth = paint.measureText(String.valueOf(contentStr.charAt(offset - 1)));
                                        float preX = layout.getSecondaryHorizontal(offset - 1) - preCharWidth;
                                        // 字符重叠了
                                        if(Math.abs(preX - startX) < charWidth / 2) {
                                            startX = layout.getSecondaryHorizontal(offset);
                                        }

                                    }
                                }
                            }

                            // 获取高亮区域的开始位置
                            float highLightStartX = startX;
                            float highLightEndX = highLightStartX + charWidth + skewOffset;
                            if (TextUtils.equals(currentChar, "\n") || TextUtils.equals(currentChar, "\r")) {
                                highLightEndX = highLightStartX + getLFWidth();
                            }
                            // 获取字符的边界
                            Rect bounds = new Rect();
                            mTextView.getPaint().getTextBounds(currentChar, 0, currentChar.length(), bounds);
                            if (bounds.left < 0) {
                                // 需要加上字符边距，否则会覆盖不上选中字符
                                highLightStartX += bounds.left;
                            }
                            RectF rectF = new RectF(
                                    highLightStartX,
                                    adjustedTop,
                                    highLightEndX,
                                    adjustedBottom);
                            // 字符之间可能有缝隙，需要判断上一个rect的右坐标和当前rect的左坐标差是否需要2px
                            if (lastRectF != null && Math.abs(rectF.left - lastRectF.right) <= 2) {
                                rectF.left = lastRectF.right;

                            }
                            lastRectF = rectF;
                            Path highlight = new Path();
                            highlight.addRect(rectF, Path.Direction.CW);
                            canvas.drawPath(highlight, mHighlightPaint);


                            canvas.drawText(currentChar, startX, baseline, paint);
                            // 移动到下一个字符
                            offset += charCount;
                        }
                       /* // 从右到左书写需要另外绘制
                        if (true) {


                        } else {
                            // 只绘制当前行选中部分的文本
                            canvas.drawText(mTextView.getText().toString().toCharArray(), lineSelStart, lineSelEnd - lineSelStart, startX, baseline, paint);
                        }*/

                    }
                }
                canvas.restore();
            }
        }
    }

    public CursorRect getCursorLocation(PointF pointF) {


        Layout layout = mTextView.getLayout();
        // 获取光标的位置,返回的是位置所在的位置，不是坐标值
        int cursorPosition = mTextView.getSelectionStart();
        // 这个返回的是实际坐标值，相对文本框的x坐标
        float cursorX = (int) (layout.getPrimaryHorizontal(cursorPosition) + mTextView.getCompoundPaddingLeft());
        Log.i(TAG, "getCursorLocation cursorX:" + cursorX);
        // 获取当前行号
        int cursorLine = layout.getLineForOffset(cursorPosition);
        int pointLine = cursorLine;
        isCursorTail = false;
        lastDownPoint = pointF;
        if(pointF != null) {
            pointLine = layout.getLineForVertical((int) pointF.y);
            Log.i(TAG, "getCursorLocation pointF:" + pointF.toString() + "=getLineCount=" + layout.getLineCount() + "=cursorPosition=" + cursorPosition
                    + "=length=" + mTextView.getText().length() +"=cursorX="+cursorX +"=getLineTop="+(layout.getLineTop(cursorLine) + getTextViewPaddingTop()));

        }
        Log.i(TAG,"getCursorLocation cursorLine:"+cursorLine+"=="+pointLine);
        if(pointF != null && layout.getLineCount() >1) {
            // 点的行号小于光标行号，说明换行了，需要重写计算,拖动小水滴会出现这种状况
            if(pointLine < cursorLine) {
                cursorLine = pointLine;
                float lineRight = layout.getLineRight(cursorLine) + mTextView.getCompoundPaddingLeft();
                cursorX = lineRight;
                isCursorTail = true;

            } else if(pointLine == cursorLine  && cursorPosition + 1 <= mTextView.getText().length()) {
                // 下一个位置的行号对不上当前行号，需要确认是否要绘制队尾光标，点击会出现这种状况
                int nextPositionLine = layout.getLineForOffset(cursorPosition + 1);
                if(nextPositionLine != cursorLine) {
                    float lineRight = layout.getLineRight(cursorLine) + mTextView.getPaddingLeft();
                    // 点击位置靠近右侧，光标要在队尾
                    if(pointF.x - cursorX > lineRight - pointF.x) {
                        cursorX = lineRight;
                        isCursorTail = true;
                        cursorPosition += 1;
                        Selection.setSelection((Spannable) mTextView.getText(),cursorPosition);
                    }

                }
            }
        }

        // 行号大于1,且下一位的位置小于等于总长度需要确认队尾和队首
        // 因为队尾光标定位不到，总会跳到上个字符，需要特殊处理
        /*if(pointF != null &&  layout.getLineCount() >1 && cursorPosition + 1 <= mTextView.getText().length()
                && !isPositionContainsTab(cursorPosition)) {
            int nextPositionLine = layout.getLineForOffset(cursorPosition + 1);
            Log.i(TAG,"getCursorLocation nextPositionLine:"+nextPositionLine+"=="+cursorLine);
            // 下一个位置的行号对不上当前行号，需要确认是否要绘制队尾光标
            if(nextPositionLine != cursorLine) {
                float lineRight = layout.getLineRight(cursorLine) + mTextView.getCompoundPaddingLeft();
                Log.i(TAG,"getCursorLocation lineRight:"+lineRight);
                // 点击位置靠近右侧，光标要在队尾
                if(pointF.x - cursorX > lineRight - pointF.x) {
                    cursorX = lineRight;
                    isCursorTail = true;
                    cursorPosition += 1;
                    if(mTextView instanceof EditText) {
                        ((EditText)mTextView).setSelection(cursorPosition);
                    } else {
                        Selection.setSelection(((EditText) mTextView).getText(), cursorPosition);
                    }
                }
            }
            // 说明换到下一行了，位置需要改变
            else if (pointF.x > cursorX && pointF.y < layout.getLineTop(cursorLine) + getTextViewPaddingTop() - mTextView.getScrollY()) {
                cursorX = layout.getLineRight(cursorLine - 1) + mTextView.getCompoundPaddingLeft();
                // 需要退到上一行
                cursorLine = cursorLine - 1;
                isCursorTail = true;
            }

        }*/

        float textSize = mTextView.getTextSize();
        // 获取光标粗细
        int cursorWidth = (int) (textSize / 15);
        mPaint.setStrokeWidth(cursorWidth); // 设置画笔的线宽



        // 为首位和末尾的光标添加偏移量
       /* if (isAtStart) {
            if (isGravityRight) {
                cursorX -= cursorWidth / 2;
                // 空内容，且斜体的情况下要减去倾斜值，否则光标无法显示
                if (TextUtils.isEmpty(mTextView.getText().toString()) && isItalic) {
                    cursorX -= skewOffset;
                }
            } else {
                // 如果是首位，向右偏移一半光标宽度
                cursorX += cursorWidth / 2;
            }
        } else if (isAtEnd) {
            if (isGravityRight) {
                cursorX += cursorWidth / 2;

            } else {
                // 如果是末尾，向左偏移一半光标宽度
                cursorX -= cursorWidth / 2;
            }
        }*/


        float assentLine = layout.getLineAscent(cursorLine);
        float baseLine = layout.getLineBaseline(cursorLine);


        CursorRect cursorRect = new CursorRect(cursorX, baseLine + assentLine + getTextViewPaddingTop(),
                baseLine + layout.getLineDescent(cursorLine) + getTextViewPaddingTop());
        setCursorRect(cursorRect);
        return cursorRect;
    }



    /**
     * 绘制光标
     * @param canvas
     */
    public void drawCursor(Canvas canvas) {
        if (!mTextView.hasFocus()) {
            return;
        }
        switch (action) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                if (!isTextSelected) {
                    CursorRect cursorPoint = getCursorLocation(lastDownPoint);


                    Log.i(TAG, "Edit onDraw cursorX:" + cursorPoint.cursorX);

                    canvas.drawLine(cursorPoint.cursorX, cursorPoint.top , cursorPoint.cursorX, cursorPoint.bottom , mPaint);
                }
            }
            break;
        }
    }

    /**
     * 绘制高亮区域
     * @param canvas
     */
    public void drawHighlight(Canvas canvas) {
        int[] tempLocation = new int[]{mPositionX, mPositionY};
//        mTextView.getLocationInWindow(tempLocation);
//        drawTextViewIcon(canvas);
        if (!mTextView.isFocused()) {
            cancelSelection();
            return;
        }
        if (lastGravity != mTextView.getGravity()) {
            lastGravity = mTextView.getGravity();
            if (mCursorHandleView.isShow()) {
                hideCursorHandle();
                dismissSelectionMenu();
            }
           /* if (mSelStart != mSelEnd) {
                updateSelectionPopWindows();
            }*/

        }
       /* // 获取高亮路径
        Path highlight = getUpdatedHighlightPath();
        if (highlight != null) {
            canvas.save();
            // 设置裁剪区域，排除padding区域
            int paddingLeft = mTextView.getCompoundPaddingLeft();
            int paddingTop = getTextViewPaddingTop();
            int paddingRight = mTextView.getCompoundPaddingRight();
            int paddingBottom = mTextView.getCompoundPaddingBottom();

            // 创建裁剪区域，只在内容区域绘制
            canvas.clipRect(
                    paddingLeft + mTextView.getScrollX(),
                    paddingTop + mTextView.getScrollY(),
                    mTextView.getWidth() - paddingRight + mTextView.getScrollX(),
                    mTextView.getHeight() - paddingBottom + mTextView.getScrollY());

            // 只绘制高亮背景，不绘制文字
            canvas.drawPath(highlight, mHighlightPaint);
            canvas.restore();
        }
        TextPaint paint = mTextView.getPaint();
        int originalColor = mTextView.getCurrentTextColor();

        // 先绘制未选中的文本
        paint.setColor(originalColor);*/

    }

    static final int LEFT = 0;
    static final int TOP = 1;
    static final int RIGHT = 2;
    static final int BOTTOM = 3;

    public void onSelectionChanged(int selStart, int selEnd) {
        isTextSelected = selStart != selEnd;
        boolean isTextVisible = mTextView.getVisibility() == View.VISIBLE;
        if (!isTextVisible || selStart != selEnd) {
            hideCursorHandle();
        }
        // 当前TextView可见，且在选中状态，如果选中的区域不一致，需要更新选中状态，主要可能是蓝牙键盘更新选中区域
        if (isTextVisible && selStart != selEnd ) {

            int tempSelStart = Math.min(mSelEnd, mSelStart);
            int tempSelEnd = Math.max(mSelEnd, mSelStart);
            if (tempSelStart != selStart || selEnd != tempSelEnd) {
                mSelStart = selStart;
                mSelEnd = selEnd;

            }
            if(mSelectionMenu.isShowing()) {
                updateSelectionPopWindows();
            }

        }

        if((selStart == selEnd && !isShowingSelectionMenu) || !isTextVisible) {
            mSelEnd = mSelStart = selStart;
            if (mCursorIndex != mSelEnd) {
                hideCursorHandle();
            }
            mCursorIndex = mSelStart;
            dismissPopupView();
        }
        if(isTextVisible && selStart == selEnd && !isDownCursorHandle) {
            mTextView.postInvalidate();
        }
    }

    // 处理光标、拖柄移动后对TextView的滚动效果
    public boolean bringTextIntoView() {

        int selStart = Selection.getSelectionStart(mTextView.getText());
        int selEnd = Selection.getSelectionEnd(mTextView.getText());
        boolean isTail = false;
        // 光标定位
        if (selEnd == selStart && mCursorRect != null) {
            isTail = mCursorHandleView.isCursorHandleTail();
            if (isTail) {
                mTextView.bringPointIntoView(selEnd - 1);
            }
            return isTail;
        }
        if (isDownStartHandle) {
            isTail = mStartHandleView.isSelHandleTail();
            if (isTail) {
                mTextView.bringPointIntoView(mSelStart - 1);
            }
            return isTail;
        }
        if (isDownEndHandle) {
            isTail = mEndHandleView.isSelHandleTail();
            if (isTail) {
                mTextView.bringPointIntoView(mSelEnd - 1);
            }
            return isTail;
        }
        return false;
    }


    // 直接获取paddingTop会不准，有时候会是0，getBaseline获取到的的值包含了真正的paddingTop
    // 而layout.getLineBaseline(0)获取的不包含paddingTop
    public int getTextViewPaddingTop() {
        if (mTextView == null) {
            return 0;
        }

        return mTextView.getBaseline() - mTextView.getLayout().getLineBaseline(0);
    }

    /**
     * 设置选中菜单是否显示
     * @param enabled
     */
    public void setSelectionMenuEnabled(boolean enabled) {
        selectionMenuEnabled = enabled;

    }


    /**
     * 设置光标菜单是否显示
     * @param enabled
     */
    public void setCursorMenuEnabled(boolean enabled) {
        cursorMenuEnabled = enabled;

    }

    /**
     * 设置光标拖动手柄是否显示
     * @param enabled
     */
    public void setCursorHandleEnabled(boolean enabled) {
        cursorHandleEnabled = enabled;
    }

    private int getSelStartLine() {
        if (mStartHandleView.isStart) {
            return mStartHandleView.getCurrentLine();
        } else {
            return mEndHandleView.getCurrentLine();
        }
    }

    private int getSelEndLine() {
        if (!mStartHandleView.isStart) {
            return mStartHandleView.getCurrentLine();
        } else {
            return mEndHandleView.getCurrentLine();
        }
    }

    // 是否选中边界
    private boolean isSelectionBr() {
        if(!mStartHandleView.isStart) {
            return mStartHandleView.isBr;
        } else {
            return mEndHandleView.isBr;
        }
    }



    /**
     * 文字变化监听
     * @param s 变化的内容
     * @param start 变化的开始位置，增加是上一个变化的位置，删除，是当前位置
     * @param before 删除的字符数
     * @param count 增加的字符数
     */
    @SuppressLint("CheckResult")
    private void textChanged(CharSequence s){
        Log.i(TAG, "textChanged");
        updateDrawTextValue(s);


    }

    /**
     * 更新需要位置内容的值
     * @param s
     * @param start
     * @param before
     * @param count
     */
    private void updateDrawTextValue(CharSequence s) {
        Log.i(TAG, "updateDrawTextValue");

        if(mHighLightMap == null) {
            mHighLightMap = new ConcurrentHashMap<>();
        }
        mHighLightMap.clear();

        if(TextUtils.isEmpty(mTextView.getText())) {
            return;
        }
        Layout layout = mTextView.getLayout();
        if(layout == null) {
            return;
        }
        try {
            int lineCount = layout.getLineCount();

            int paddingLeft = mTextView.getCompoundPaddingLeft();
            float spacingExtra = mTextView.getLineSpacingExtra(); // 行间距值，单位像素（根据需求调整）
            int paddingTop = getTextViewPaddingTop();
            // 获取最后一行
            int finalLine = layout.getLineForOffset(mTextView.getText().length());

            String contentStr = mTextView.getText().toString();

            for (int line = 0; line < lineCount; line++) {
                HighLightLineInfo highLightLineInfo = getHighLightInfo(contentStr, layout, line, finalLine, paddingLeft, paddingTop, spacingExtra);
                mHighLightMap.put(line, highLightLineInfo);

            }
            // 因为多线程原因，可能会出现行数变化，导致数组越界，此时需要清空map，在绘制的时候重新获取
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
            mHighLightMap.clear();
        }

    }

    private HighLightLineInfo getHighLightInfo(String contentStr, Layout layout, int line, int finalLine, int paddingLeft, int paddingTop, float spacingExtra) {
        Path linePath = new Path();
        HighLightLineInfo highLightLineInfo = new HighLightLineInfo();
        Paint paint = mTextView.getPaint();

        float lineLeft = layout.getLineLeft(line);
        float startX = lineLeft;
        float endX = layout.getLineRight(line) + getSkewOffset();
        float lineTop = layout.getLineTop(line);
        float lineBottom = layout.getLineBottom(line);
        float baseline = layout.getLineBaseline(line);
        int start = layout.getLineStart(line);
        int end = layout.getLineEnd(line);
        float LFWidth = getLFWidth();
        // 调整顶部和底部间距,间距可能还需要调整
        float adjustedTop = lineTop;
        // 文本的最后一行不需要减去行间距
        float adjustedBottom = (line == finalLine ? lineBottom : lineBottom - spacingExtra);

        String endChar = String.valueOf(contentStr.charAt(end -1));
        // 加上换行符的宽度
        if (TextUtils.equals(endChar, "\n")
                || TextUtils.equals(endChar, "\r")) {
            endX += LFWidth;
        }

        RectF rectF = new RectF(
                startX,
                adjustedTop,
                endX,
                adjustedBottom);
        linePath.addRect(rectF, Path.Direction.CW);

        highLightLineInfo.linePath = linePath;



        float drawTextX = lineLeft;
        if(layout.getParagraphDirection(line) == -1) {
            // 加上换行符的宽度
            if (TextUtils.equals(endChar, "\n")
                    || TextUtils.equals(endChar, "\r")) {
                end = end -1;
            }
        }
        highLightLineInfo.x = drawTextX;
        highLightLineInfo.y = baseline;
        highLightLineInfo.start = start;
        highLightLineInfo.end = end;
        return highLightLineInfo;
    }

    public int findEmojiStart(String text, int offset) {
        // 检查是否在低代理项位置
        if (offset > 0 && offset < text.length() &&
                Character.isLowSurrogate(text.charAt(offset)) &&
                Character.isHighSurrogate(text.charAt(offset - 1))) {
            return offset - 1; // 返回高代理项位置
        }
        return offset;
    }

    public void onSetFontSize(int unit, float size) {
        if(!TextUtils.isEmpty(mTextView.getText())) {
            textChanged(mTextView.getText());
        }

    }

    /**
     * 拖柄内部View
     */
    public class SelHandleView extends View {

        private final String TAG = "SelHandleView";

        protected Bitmap mBitmap;
        private PopupWindow mContainer;
        int handleWidth;
        int handleHeight;

        private int selOffset;

        boolean isStart;
        private int lastScrollY = 0;
        private int lastScrollX = 0;


        SelHandleListener mSelListener;

        //是否时单纯的光标拖动手柄，不是选中的效果
        boolean isCursorHandle = false;

        // 相对TextView的坐标
        private float xOnTextView = 0;
        private float yOnTextView = 0;

        // 字符边界
        private float boundsLeft = 0;

        Paint mPaint = new Paint();

        // 是否是边界
        private boolean isBr = false;

        private float padding;

        // 整个显示区域的尺寸
        private int width;
        private int height;

        public SelHandleView(Context context, Bitmap bitmap) {
            super(context);
            mBitmap = bitmap;
            init();
        }

        private void init() {
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setFilterBitmap(true);
            mPaint.setDither(true);

            setWillNotDraw(false);
            handleWidth = mBitmap.getWidth();
            handleHeight = mBitmap.getHeight();
            padding = mTextView.getResources().getDimension(com.ratta.supernote.note.plugincore.R.dimen.dp_px_10);
            // 显示区域尺寸需要加上padding
            width = (int) (handleWidth + padding);
            height = (int) (handleHeight + padding);
            mContainer = new PopupWindow(this, ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            mContainer.setContentView(this);
            mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
            mContainer.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            mContainer.setOutsideTouchable(true);
            mContainer.setFocusable(false);
            // 修改PopupWindow配置
            mContainer.setAnimationStyle(0); // 禁用动画
            mContainer.setClippingEnabled(false); // 禁用自动裁剪
            mContainer.setTouchInterceptor(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    boolean isDown = isDownHandle(event);
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_POINTER_DOWN:
                            if (event.getPointerCount() > 1) {
                                return false;
                            }
/*                        case MotionEvent.ACTION_DOWN:
                            if (isDown) {
                                return false;
                            }

                            break;*/

                        case MotionEvent.ACTION_OUTSIDE:
                            if (mSelListener != null) {
                                mSelListener.onDownHandle(false);
                                mSelListener.onTouchUp();
                            }
                            // 自己处理点击事件，返回true
                            return true;

                    }
                    return false;
                }
            });

            mTextView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
                @Override
                public void onScrollChanged() {
                    int scrollY = mTextView.getScrollY();
                    // 判断上一次的滚动距离是否相同，避免重复刷新
                    if (mContainer.isShowing() && (lastScrollY != scrollY || lastScrollX != mTextView.getScrollX())) {
                        // 需要触发光标和非光标的小水滴，两种显示方式不一样
                        if (isCursorHandle) {
                            show(false,
                                    isCursorHandleTail(new PointF(mCursorRect.cursorX,
                                            (mCursorRect.top + mCursorRect.bottom) / 2)));
                        } else {
                            show(false);
                        }

                    }
                }
            });
        }

        public void setDrawable(Bitmap bitmap) {
            this.mBitmap = bitmap;

        }

        public void setStart(boolean start) {
            if (start != isStart) {
                isStart = start;
            }

        }

        public boolean isStart() {
            return isStart;
        }

        public void setCursorHandle(boolean cursorHandle) {
            isCursorHandle = cursorHandle;
        }

        @Override
        protected void onDraw(@NonNull Canvas canvas) {
            Log.i(TAG, "handleView onDraw");

            // 创建一个矩形区域，表示View的可绘制区域
            Rect destRect = new Rect(0, 0, handleWidth, handleHeight);

            // 创建一个矩形区域，表示Bitmap的区域
            Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
            Log.i(TAG,"onDraw handleWidth:"+handleWidth+"=="+handleHeight);

            // 将Bitmap绘制到View的区域内，确保完整显示
            canvas.drawBitmap(mBitmap, srcRect, destRect, mPaint);
        }

        // 获取手柄显示区域
        public Rect getHandleRect() {
            return new Rect(lastLocationX, lastLocationY,
                    lastLocationX + handleWidth, lastLocationY + handleHeight);
        }

        private int lastLocationX;
        private int lastLocationY;

        public void show(boolean isTop) {


            boolean isBr = false;
            // 光标手柄，选中位置不是开头，当前位置行号和上一个位置行号说明在边界
            if (!isStart && selOffset > 0 /*&& !isPositionContainsTab(selOffset)*/) {
                Layout layout = mTextView.getLayout();
                int selLine = layout.getLineForOffset(selOffset);
                if (layout.getParagraphDirection(selLine) == Layout.DIR_LEFT_TO_RIGHT) {
                    int selPreLine = layout.getLineForOffset(selOffset - 1);
                    isBr = selLine != selPreLine;
                }
            }
            update(true, isTop, isBr);

        }

        public void show(boolean isTop, boolean isBr) {
            update(true, isTop, isBr);
        }

        public boolean isCursorHandleTail() {
            if (!isCursorHandle) {
                return false;
            }
            if (selOffset >= mTextView.getText().length()) {
                return false;
            }
            if (selOffset > 0 && !isPositionContainsTab(selOffset)) {
                Layout layout = mTextView.getLayout();
                if(layout == null) {
                    return false;
                }
                int selLine = layout.getLineForOffset(selOffset);
                int selPreLine = layout.getLineForOffset(selOffset - 1);

                if (selLine != selPreLine) {
                    // 需要减去滚动距离，否则计算有问题
                    float lineTop = layout.getLineTop(selLine);
                    float lineBottom = layout.getLineBottom(selLine);
                    if (yOnTextView > lineTop && yOnTextView < lineBottom) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
            return false;
        }

        // 光标是否在末尾
        public boolean isCursorHandleTail(PointF pointF) {
            if (!isCursorHandle) {
                return false;
            }
            if (selOffset >= mTextView.getText().length()) {
                return false;
            }
            // 光标手柄，选中位置不是开头，当前位置行号和上一个位置行号说明在边界
            if (selOffset > 0 && !isPositionContainsTab(selOffset)) {
                Layout layout = mTextView.getLayout();
                int selLine = layout.getLineForOffset(selOffset);
                int selPreLine = layout.getLineForOffset(selOffset - 1);
                if (selLine != selPreLine) {
                    // 需要减去滚动距离，否则计算有问题
                    float lineTop = layout.getLineTop(selLine) + getTextViewPaddingTop() - mTextView.getScrollY();
                    float lineBottom = layout.getLineBottom(selLine) + getTextViewPaddingTop() - mTextView.getScrollY();

                    if (pointF.y > lineTop && lineBottom > pointF.y) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
            return false;
        }

        private void update(boolean isShow, boolean isTop, boolean isBr) {

            int[] selLocation = getSelHandlePosition(selOffset, isTop, isBr, true);


            int xOffset = 0;
            if (isCursorHandle) {
                xOffset = handleWidth / 2;
            } else if (mBitmap == selStartBitmap) {
                xOffset = handleWidth;
            }
            lastLocationX = selLocation[0] - xOffset;

            lastLocationY = selLocation[1] + (isTop ? -handleHeight : 0);
            // 光标水滴不需要隐藏
            if (!isCursorHandle) {
                boolean isHandleInTextView = isHandleInTextView(new float[]{lastLocationX, lastLocationY});
                setVisibility(isHandleInTextView ? VISIBLE : GONE);
            }

            Log.i(TAG,"update lastLocationX:"+lastLocationX+", lastLocationY"+lastLocationY);

            if (mContainer.isShowing()) {
                mContainer.update(lastLocationX, lastLocationY, -1, -1);
            } else if(isShow){
                mContainer.showAtLocation(mTextView, Gravity.START | Gravity.TOP, lastLocationX, lastLocationY);
            }
        }

        public void update(boolean isTop) {
            update(false, isTop, false);
        }

        private void updatePosition(int x, int y) {

            lastLocationX = x;
            lastLocationY = y;
            Log.i(TAG,"update lastLocationX:"+lastLocationX+", lastLocationY"+lastLocationY);
            // 光标水滴不需要隐藏
            if (!isCursorHandle) {
                boolean isHandleInTextView = isHandleInTextView(new float[]{x, y});
                setVisibility(isHandleInTextView ? VISIBLE : GONE);
            }
            if (mContainer.isShowing()) {
                mContainer.update(x, y, -1, -1);
            } /*else {
                mContainer.showAtLocation(mTextView, Gravity.START | Gravity.TOP, x, y);
            }*/
        }

        public void dismiss() {
            if (mContainer != null && mContainer.isShowing()) {
                mContainer.dismiss();
                lastLine = -1;
            }
        }

        public boolean isShow() {
            if (mContainer == null) {
                return false;
            }
            return mContainer.isShowing();
        }

        // 拖柄是否在TextView内
        private boolean isHandleInTextView(float[] position) {
            if (position == null || position.length < 2) {
                return false;
            }
            Layout layout = mTextView.getLayout();
            if (isStart && layout.getParagraphDirection(lastLine) != -1) {
                position[0] += handleWidth;
            }
            int[] textViewPosition = new int[]{mPositionX, mPositionY};
//            mTextView.getLocationInWindow(textViewPosition);
            Log.i(TAG,"isHandleInTextView position:"+Arrays.toString(position)+"=="+Arrays.toString(textViewPosition) +"=="+mTextView.getCompoundPaddingLeft());
            return position[0] >= textViewPosition[0] + mTextView.getCompoundPaddingLeft() + boundsLeft
                    && position[0] <= textViewPosition[0] + mTextView.getWidth() - mTextView.getCompoundPaddingRight()
                    && position[1] >= textViewPosition[1] + getTextViewPaddingTop()
                    && position[1] <= textViewPosition[1] + mTextView.getHeight() - mTextView.getPaddingBottom();
        }

        public int getHandleWidth() {
            return handleWidth;
        }

        public int getHandleHeight() {
            return handleHeight;
        }

        public void setHandleWidth(int handleWidth) {
            this.handleWidth = handleWidth;
            // 显示区域尺寸需要加上padding
            width = (int) (handleWidth + padding);
        }

        public void setHandleHeight(int handleHeight) {
            this.handleHeight = handleHeight;
            // 显示区域尺寸需要加上padding
            height = (int) (handleHeight + padding);
        }

        public void setSelOffset(int selOffset) {
            this.selOffset = selOffset;
        }

        public void setSelListener(SelHandleListener mSelListener) {
            this.mSelListener = mSelListener;
        }

        private int lastTouchX;
        private int lastTouchY;

        private long lastTouchTime = 0;


        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int[] windowLocation = convertRawToWindow(event);
            int rawX = windowLocation[0];
            int rawY = windowLocation[1];
            switch (event.getAction()) {
                case MotionEvent.ACTION_POINTER_DOWN:
                    if (event.getPointerCount() > 1) {
                        return false;
                    }
                case MotionEvent.ACTION_DOWN:
                    boolean isDown = isDownHandle(event);
                    if (mSelListener != null) {
                        mSelListener.onDownHandle(true);
                    }
                  /*  if (!isDown) {
                        return false;
                    }*/
                    lastTouchX = rawX;
                    lastTouchY = rawY;
                    lastTouchTime = System.currentTimeMillis();
                    return true;

                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:

                    int deltaX = rawX - lastTouchX;
                    int deltaY = rawY - lastTouchY;

                    moveHandle(deltaX, deltaY);

                    if (mSelListener != null) {
                        if (event.getAction() == MotionEvent.ACTION_UP
                                || MotionEvent.ACTION_CANCEL == event.getAction()
                        ) {
                            if (System.currentTimeMillis() - lastTouchTime <= 200
                                    && deltaX < 10 && deltaY < 10) {
                                mSelListener.onClickHandle();
                            }
                            mSelListener.onTouchUp();

                        }
                    }

                    return true;
            }

            return super.onTouchEvent(event);
        }

        private int[] convertRawToWindow(MotionEvent event) {
            int[] windowLocation = new int[2];
            

            // 计算相对于窗口的坐标
            windowLocation[0] = (int) event.getRawX() - (mPositionXOnScreen - mPositionX);
            windowLocation[1] = (int) event.getRawY() - (mPositionYOnScreen - mPositionY);

            return windowLocation;
        }

        // 是否点击在手柄上
        private boolean isDownHandle(MotionEvent event) {
            return event.getRawX() - lastLocationX < handleWidth
                    && event.getRawX() - lastLocationX > 0
                    && event.getRawY() - lastLocationY < handleHeight
                    && event.getRawY() - lastLocationY > 0;
        }

        int lastLine = -1;

        private void moveHandle(int deltaX, int deltaY) {
            float moveOffset = mTextView.getTextSize() / 3;
            if (Math.abs(deltaX) <= moveOffset && Math.abs(deltaY) <= moveOffset) {
                return;
            }
            Layout layout = mTextView.getLayout();
            int[] textViewLocation = new int[]{mPositionX, mPositionY};
//            mTextView.getLocationInWindow(textViewLocation);

            int textViewX = textViewLocation[0] + mTextView.getCompoundPaddingLeft();
            int textViewY = textViewLocation[1] + getTextViewPaddingTop();

            // 点击事件相对文本框的位置
            int xInTextView = lastTouchX - textViewX + deltaX + mTextView.getScrollX();
            // 添加边界检查，确保行号在有效范围内
            int lineCount = layout.getLineCount();
            lastLine = lastLine < 0 || lastLine >= lineCount ? layout.getLineForOffset(selOffset) : lastLine;
            int lineTop = layout.getLineTop(lastLine);
            // 这个逻辑需要测试（lineTop + textViewY - lastLocationY）计算出的是handle左上角离当前行高的位置
            int yInTextView = lastTouchY + (lineTop + textViewY - lastLocationY) - textViewY + deltaY;
            Log.i(TAG,"moveHandle lastLine:"+lastLine+"=lintTop="+lineTop+"=textViewY="+textViewY+"=lastLocationY="+lastLocationY+"=lastTouchY="+lastTouchY);
            // 获取所在的文字行数
            int line = layout.getLineForVertical(yInTextView);
            Log.i(TAG, "moveHandle line:" + line + "=yInTextView=" + yInTextView
                    + "=getLineTop=" + layout.getLineTop(lastLine)
                    + "=getLineBottom=" + layout.getLineBottom(lastLine));

            yOnTextView = (float) (layout.getLineTop(lastLine) + layout.getLineBottom(lastLine)) / 2;


            // 获取所在行数水平方向的偏移量
            int offset = layout.getOffsetForHorizontal(line, xInTextView);


            // 检查是否接近行尾
            float lineRight = layout.getLineRight(line);
            Log.i(TAG, "moveHandle lineRight:" + lineRight + "==" + xInTextView);
            boolean isBr = false;
            if(layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT) {
                // 1.拖动的位置大于行有边界
                // 2.当前行等于上一行，（不等于就是换行了）
                if (xInTextView > lineRight && line == lastLine) {
                    int lineEndOffset = layout.getLineEnd(line);
                    offset = lineEndOffset;
                    isBr = true;

                }
            }
            this.isBr = isBr;


            // 是否有改变
            boolean isChange = false;
            int tempX = lastTouchX;
            int tempY = lastTouchY;
            if (selOffset != offset) {
                tempX = lastTouchX + deltaX;
                isChange = true;
            }
            if (lastLine != line) {
                lastLine = line;
                tempY = lastTouchY + deltaY;
                isChange = true;
            }

            if (!isChange) {
                return;
            }

            boolean isSelUpdate = true;
            if (isCursorHandle) {
                lastDownPoint = new PointF(xInTextView, yOnTextView);
            }
            if (mSelListener != null) {
                isSelUpdate = mSelListener.onSelOffsetChange(offset, isBr);
            }
            // 需要考虑几种情况
            // 1.不是光标，isSelUpdate不需要更新，说明是选中状态，那就不需要刷新小水滴
            // 2.是光标，在同一行，也不需要更新
            // 只有在不是同一样，是光标的情况下才需要更新
            if (!isSelUpdate && (lastLine == line || !isCursorHandle)) {
                return;
            }

            selOffset = offset;
            lastTouchX = tempX;
            lastTouchY = tempY;

            boolean isTop = false;
            // 获取光标位置
            int[] handlePosition = getSelHandlePosition(offset, isTop, isBr, false);
            // 行数变了，但是位置没变，需要再刷新一次TextView, 因为isSelUpdate=false是不会刷新TextView的
            // 否则光标位置可能不对
            if(!isSelUpdate) {
                mTextView.postInvalidate();
            }
            int xOffset = 0;
            if (isCursorHandle) {
                xOffset = handleWidth / 2;
            } else if (mBitmap == selStartBitmap) {
                xOffset = handleWidth;
            }

            updatePosition(handlePosition[0] - xOffset, handlePosition[1] + (isTop ? -handleHeight : 0));
            if(mSelListener != null) {
                mSelListener.onUpdateHandleMenuPosition(isBr);
            }
//            invalidate();
            postInvalidate();

        }


        // 获取光标拖柄的位置坐标
        private int[] getCursorHandlePosition(int selOffset, boolean isTop, boolean isBr, boolean isShow) {
            Layout layout = mTextView.getLayout();
            if (layout == null || selOffset < 0 || selOffset > mTextView.getText().length()) {
                return new int[]{0, 0};
            }
            lastScrollY = mTextView.getScrollY();

            PointF pointF = null;
            CursorRect cursorRect = null;
            // 获取行号，如果换行，selOffset需要-1，否则手柄位置会不对
            int line = layout.getLineForOffset(isBr ? selOffset - 1 : selOffset);
            lastLine = line;
            if (isBr) {
                // 获取X坐标（需要包含padding）,如果换行，需要获取最右边位置
                float xHorizontal = (isBr ? layout.getLineRight(line) : layout.getPrimaryHorizontal(selOffset)) + mTextView.getCompoundPaddingLeft();

                float lineBottom = layout.getLineBottom(line) ;
                float linTop = layout.getLineTop(line);
                pointF = new PointF(xHorizontal, (linTop + lineBottom) / 2);
            }
            cursorRect = getCursorLocation(pointF);


            if (cursorRect == null) {
                return null;
            }

            yOnTextView = (cursorRect.bottom + cursorRect.top) / 2;

            // 转换为屏幕绝对坐标
            int[] location = new int[]{mPositionX, mPositionY};
//            mTextView.getLocationInWindow(location);

            // 获取TextView的滚动偏移量

            lastScrollX = mTextView.getScrollX();
            Log.i(TAG,"getCursorHandlePosition location:"+Arrays.toString(location) +
                    ", cursorX:"+cursorRect.cursorX+", lastScrollX:"+lastScrollX);

            // 获取的光标小水滴的cursorX已经加上了padding，所以不需要再计算padding
            return new int[]{
                    (int) (location[0] + cursorRect.cursorX - lastScrollX),
                    (int) (location[1] + cursorRect.bottom - lastScrollY)
            };
        }

        // 核对小水滴的图标，需要核对当前字符是从右往左还是从左往右书写，不同的书写方式，小水滴图标是相反的
        private void checkDrawable() {
            if(isCursorHandle) {
                return;
            }
            if (TextUtils.isEmpty(mTextView.getText())) {
                return;
            }
            int charOffset = selOffset;
            if (selOffset == mTextView.length()) {
                charOffset = selOffset - 1;
            }
            if (charOffset < 0) {
                charOffset = 0;
            }
            Layout layout = mTextView.getLayout();

            boolean isCharRtl = layout.isRtlCharAt(charOffset);
            if (isStart) {
                if (isCharRtl) {
                    if (mBitmap != selEndBitmap) {
                        mBitmap = selEndBitmap;
                        postInvalidate();
                    }
                } else {
                    if (mBitmap != selStartBitmap) {
                        mBitmap = selStartBitmap;
                        postInvalidate();
                    }
                }
            } else {
                if (isCharRtl) {
                    if (mBitmap != selStartBitmap) {
                        mBitmap = selStartBitmap;
                        postInvalidate();
                    }
                } else {
                    if (mBitmap != selEndBitmap) {
                        mBitmap = selEndBitmap;
                        postInvalidate();
                    }
                }
            }
        }

        /**
         * 获取手柄的坐标
         *
         * @param selOffset 文字光标所在位置
         * @param isBr      是否换行，selOffset在末尾，会换行，手柄位置就对不上，校正位置
         * @param  isShow 是否是刚开始显示用的
         * @return 返回的坐标是文字光标的左上角
         */
        private int[] getSelHandlePosition(int selOffset, boolean isTop, boolean isBr, boolean isShow) {
            Log.i(TAG, "getSelHandlePosition isBr:"+isBr);
            this.isBr = isBr;

            if (isCursorHandle) {
                int[] handleLocation = getCursorHandlePosition(selOffset, isTop, isBr, isShow);
                if (handleLocation != null) {
                    return handleLocation;
                }
            }
            checkDrawable();

            Layout layout = mTextView.getLayout();
            if (layout == null || selOffset < 0 || selOffset > mTextView.getText().length()) {
                return new int[]{0, 0};
            }
            float spacingExtra = mTextView.getLineSpacingExtra(); // 行间距值，单位像素（根据需求调整）

            // 获取行号，如果换行，selOffset需要-1，否则手柄位置会不对
            int line = layout.getLineForOffset(isBr ? selOffset - 1 : selOffset);
            lastLine = line;
            float lineBottom = layout.getLineBottom(line);
            // 获取最后一行
            int finalLine = layout.getLineForOffset(mTextView.getText().length());
            lineBottom = finalLine == line ? lineBottom : lineBottom - spacingExtra;
            // 获取行的y轴坐标
            float yLine = isTop ? layout.getLineTop(line) : lineBottom;
            yOnTextView = (layout.getLineTop(line) + lineBottom) / 2;
            final boolean isRtlChar = layout.isRtlCharAt(selOffset);
            final boolean isRtlParagraph = layout.getParagraphDirection(line) == -1;

            float xHorizontal = 0;
            // 需要判断行的书写方向是否和文字所在的书写方向是一致，比如都是从左到右，如果不一致，需要调用getSecondaryHorizontal
            if (isRtlChar == isRtlParagraph) {
                // 获取X坐标（不包含padding）,如果换行，需要获取最右边位置
                xHorizontal = (isBr ? layout.getLineRight(line) : layout.getPrimaryHorizontal(selOffset));
            } else {
                xHorizontal = layout.getSecondaryHorizontal(selOffset);
            }

            xOnTextView = xHorizontal;


            // 转换为屏幕绝对坐标
            int[] location = new int[]{mPositionX, mPositionY};
//            mTextView.getLocationInWindow(location);
            // 获取TextView的滚动偏移量
            lastScrollY = mTextView.getScrollY();
            int lineStart = layout.getLineStart(line);
            int lineEnd = layout.getLineEnd(line);
            boundsLeft = 0;
           boolean isStart = mBitmap == selStartBitmap;
            if (!isStart) {

                // 需要判断当前行是否只有一个换行符，如果是，需要加上一个换行符的宽度
                if (lineEnd - lineStart == 1
                        && (mTextView.getText().charAt(lineStart) == '\n'
                        || mTextView.getText().charAt(lineStart) == '\r')) {
                    xHorizontal += getLFWidth();
                } else {
                    // 需要考虑字体是否倾斜，倾斜需要加上倾斜偏移量
                    xHorizontal += getSkewOffset();
                    // 加上换行符的宽度
                    if (!isRtlParagraph && selOffset > 0 && ((mTextView.getText().charAt(selOffset - 1) == '\n'
                            || mTextView.getText().charAt(selOffset - 1) == '\r'))) {
                        xHorizontal += getLFWidth();
                    }
                }
            } else {
                // 是否在行尾
                boolean isSelLineEnd = isRtlParagraph ? selOffset != lineStart : selOffset != lineEnd ;

                if (isSelLineEnd) {
                    String lineStr = mTextView.getText().toString().substring(selOffset, lineEnd);
                    // 获取字符的边界
                    Rect bounds = new Rect();
                    mTextView.getPaint().getTextBounds(lineStr, 0, lineStr.length(),bounds);
                    if(bounds.left < 0) {
                        boundsLeft = bounds.left;
                        xHorizontal += bounds.left;
                    }
                }
            }

            lastScrollX = mTextView.getScrollX();

            return new int[]{
                    (int) (location[0] + xHorizontal - lastScrollX) + mTextView.getPaddingLeft(),
                    (int) (location[1] + yLine - lastScrollY) + getTextViewPaddingTop()
            };
        }

        public int getCurrentLine() {
            if(lastLine < 0) {
                return 0;
            }
            return lastLine;
        }

        // 是否是边界
        public boolean isBr() {
            return isBr;
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {

        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(width , height);
        }


        // 选中手柄是否在队尾
        public boolean isSelHandleTail() {
            if (isCursorHandle) {
                return false;
            }
            if (selOffset > 0) {
                Layout layout = mTextView.getLayout();
                int selLine = layout.getLineForOffset(selOffset);
                if (layout.getParagraphDirection(selLine) == Layout.DIR_LEFT_TO_RIGHT) {
                    int selPreLine = layout.getLineForOffset(selOffset - 1);
                    if (selLine != selPreLine) {
                        // 需要减去滚动距离，否则计算有问题
                        float lineTop = layout.getLineTop(selLine);
                        float lineBottom = layout.getLineBottom(selLine);
                        if (yOnTextView > lineTop && yOnTextView < lineBottom) {
                            return false;
                        } else {
                            return true;
                        }
                    }
                }

            }
            return false;
        }

    }

    /**
     * 每行文字的信息
     */
    private class HighLightLineInfo {
        // 绘制的起始位置X轴坐标
        float x;
        // 绘制的起始位置y轴坐标
        float y;
        int start;
        int end;
        Path linePath;

        public HighLightLineInfo() {
        }
    }

}
